mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 06:09:01 +00:00
v3.4.0
This commit is contained in:
@@ -575,7 +575,7 @@
|
|||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 30;
|
CURRENT_PROJECT_VERSION = 31;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = BK8443AXLU;
|
DEVELOPMENT_TEAM = BK8443AXLU;
|
||||||
@@ -593,7 +593,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 3.3.4;
|
MARKETING_VERSION = 3.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner;
|
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
@@ -610,7 +610,7 @@
|
|||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 30;
|
CURRENT_PROJECT_VERSION = 31;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = BK8443AXLU;
|
DEVELOPMENT_TEAM = BK8443AXLU;
|
||||||
@@ -628,7 +628,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 3.3.4;
|
MARKETING_VERSION = 3.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner;
|
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
|||||||
@@ -186,4 +186,3 @@ enum DisplayMode: Int, CaseIterable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,44 @@ struct InfoButton: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct UninstallButton: ButtonStyle {
|
||||||
|
var isEnabled: Bool
|
||||||
|
|
||||||
|
func makeBody(configuration: Configuration) -> some View {
|
||||||
|
HStack(alignment: .center, spacing: 10) {
|
||||||
|
Image(systemName: "trash")
|
||||||
|
.foregroundColor(isEnabled ? .white.opacity(1) : .white.opacity(0.3))
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
.frame(height: 24)
|
||||||
|
.opacity(0.5)
|
||||||
|
|
||||||
|
configuration.label
|
||||||
|
.foregroundColor(isEnabled ? .white.opacity(1) : .white.opacity(0.3))
|
||||||
|
|
||||||
|
}
|
||||||
|
.frame(height: 24)
|
||||||
|
.frame(minWidth: 75)
|
||||||
|
.padding(.horizontal)
|
||||||
|
.padding(.vertical, 4)
|
||||||
|
.background(configuration.isPressed ? Color("uninstall").opacity(0.8) : Color("uninstall"))
|
||||||
|
// .background(
|
||||||
|
// configuration.isPressed ? Color("uninstall").opacity(0.8) :
|
||||||
|
// (isEnabled ? Color("uninstall") : Color.gray)
|
||||||
|
// )
|
||||||
|
.cornerRadius(8)
|
||||||
|
.onHover { over in
|
||||||
|
if over {
|
||||||
|
NSCursor.pointingHand.push()
|
||||||
|
} else {
|
||||||
|
NSCursor.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct WarningPopoverView: View {
|
struct WarningPopoverView: View {
|
||||||
var label: String
|
var label: String
|
||||||
var bodyText: String
|
var bodyText: String
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x4D",
|
||||||
|
"green" : "0x58",
|
||||||
|
"red" : "0xD7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x3E",
|
||||||
|
"green" : "0x47",
|
||||||
|
"red" : "0xB0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,8 @@ struct GeneralSettingsTab: View {
|
|||||||
@AppStorage("settings.general.brew") private var brew: Bool = false
|
@AppStorage("settings.general.brew") private var brew: Bool = false
|
||||||
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
|
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
|
||||||
@AppStorage("settings.general.selectedTheme") var selectedTheme: String = "Auto"
|
@AppStorage("settings.general.selectedTheme") var selectedTheme: String = "Auto"
|
||||||
|
@AppStorage("settings.general.selectedSort") var selectedSortAlpha: Bool = true
|
||||||
|
|
||||||
@State private var diskStatus: Bool = false
|
@State private var diskStatus: Bool = false
|
||||||
@State private var accessStatus: Bool = false
|
@State private var accessStatus: Bool = false
|
||||||
private let themes = ["Auto", "Dark", "Light"]
|
private let themes = ["Auto", "Dark", "Light"]
|
||||||
@@ -89,6 +91,53 @@ struct GeneralSettingsTab: View {
|
|||||||
.padding(.leading)
|
.padding(.leading)
|
||||||
|
|
||||||
|
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
Image(systemName: selectedSortAlpha ? "textformat.abc" : "textformat.123")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
.padding(.trailing)
|
||||||
|
.foregroundStyle(.gray)
|
||||||
|
VStack(alignment: .leading, spacing: 5) {
|
||||||
|
Text("File list sorting mode")
|
||||||
|
.font(.callout)
|
||||||
|
.foregroundStyle(.gray)
|
||||||
|
}
|
||||||
|
InfoButton(text: "When searching for app files or leftover files, the list will be sorted based on this choice", color: nil, label: "")
|
||||||
|
Spacer()
|
||||||
|
Picker("", selection: $selectedSortAlpha) {
|
||||||
|
Text("Alphabetical").tag(true)
|
||||||
|
Text("Size").tag(false)
|
||||||
|
}
|
||||||
|
.pickerStyle(SegmentedPickerStyle())
|
||||||
|
.frame(width: 200)
|
||||||
|
}
|
||||||
|
.padding(5)
|
||||||
|
.padding(.leading)
|
||||||
|
// HStack(spacing: 0) {
|
||||||
|
// Image(systemName: selectedSortAlpha ? "textformat.abc" : "textformat.123")
|
||||||
|
// .resizable()
|
||||||
|
// .scaledToFit()
|
||||||
|
// .frame(width: 20, height: 20)
|
||||||
|
// .padding(.trailing)
|
||||||
|
// .foregroundStyle(.gray)
|
||||||
|
// VStack(alignment: .leading, spacing: 5) {
|
||||||
|
// Text("\(selectedSortAlpha ? "File list sorted alphabetically" : "File list sorted by size")")
|
||||||
|
// .font(.callout)
|
||||||
|
// .foregroundStyle(.gray)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// InfoButton(text: "When searching for app files or leftover files, the list will be sorted based on this choice", color: nil, label: "")
|
||||||
|
//
|
||||||
|
// Spacer()
|
||||||
|
// Toggle(isOn: $selectedSortAlpha, label: {
|
||||||
|
// })
|
||||||
|
// .toggleStyle(.switch)
|
||||||
|
// }
|
||||||
|
// .padding(5)
|
||||||
|
// .padding(.leading)
|
||||||
|
|
||||||
|
|
||||||
// === Perms ================================================================================================
|
// === Perms ================================================================================================
|
||||||
|
|
||||||
|
|
||||||
@@ -208,7 +257,7 @@ struct GeneralSettingsTab: View {
|
|||||||
|
|
||||||
}
|
}
|
||||||
.padding(20)
|
.padding(20)
|
||||||
.frame(width: 500, height: 420)
|
.frame(width: 500, height: 460)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,18 +18,18 @@ struct InterfaceSettingsTab: View {
|
|||||||
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
|
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
|
||||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
@AppStorage("displayMode") var displayMode: DisplayMode = .system
|
@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 = true
|
||||||
@AppStorage("settings.general.dark") var isDark: Bool = true
|
@AppStorage("settings.general.dark") var isDark: Bool = true
|
||||||
@AppStorage("settings.general.popover") private var popoverStay: Bool = true
|
@AppStorage("settings.general.popover") private var popoverStay: Bool = true
|
||||||
@AppStorage("settings.general.miniview") private var miniView: Bool = true
|
@AppStorage("settings.general.miniview") private var miniView: Bool = true
|
||||||
@AppStorage("settings.general.selectedTheme") var selectedTheme: String = "Auto"
|
@AppStorage("settings.general.selectedTheme") var selectedTheme: String = "Auto"
|
||||||
@AppStorage("settings.interface.selectedMenubarIcon") var selectedMenubarIcon: String = "pear-4"
|
@AppStorage("settings.interface.selectedMenubarIcon") var selectedMenubarIcon: String = "pear-4"
|
||||||
private let themes = ["Auto", "Dark", "Light"]
|
|
||||||
@State private var isLaunchAtLoginEnabled: Bool = false
|
@State private var isLaunchAtLoginEnabled: Bool = false
|
||||||
let icons = ["externaldrive", "trash", "folder", "pear-1", "pear-1.5", "pear-2", "pear-3", "pear-4"]
|
|
||||||
|
|
||||||
@Binding var showPopover: Bool
|
@Binding var showPopover: Bool
|
||||||
@Binding var search: String
|
@Binding var search: String
|
||||||
|
let icons = ["externaldrive", "trash", "folder", "pear-1", "pear-1.5", "pear-2", "pear-3", "pear-4"]
|
||||||
|
private let themes = ["Auto", "Dark", "Light"]
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
@@ -67,7 +67,16 @@ struct InterfaceSettingsTab: View {
|
|||||||
|
|
||||||
|
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Image(systemName: displayMode.colorScheme == .dark ? "moon.fill" : "sun.max.fill")
|
Image(systemName: {
|
||||||
|
switch displayMode {
|
||||||
|
case .dark:
|
||||||
|
return "moon.fill"
|
||||||
|
case .light:
|
||||||
|
return "sun.max.fill"
|
||||||
|
case .system:
|
||||||
|
return "circle.righthalf.filled"
|
||||||
|
}
|
||||||
|
}())
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.frame(width: 20, height: 20)
|
.frame(width: 20, height: 20)
|
||||||
@@ -90,19 +99,23 @@ struct InterfaceSettingsTab: View {
|
|||||||
switch newTheme {
|
switch newTheme {
|
||||||
case "Auto":
|
case "Auto":
|
||||||
displayMode.colorScheme = nil
|
displayMode.colorScheme = nil
|
||||||
if isDarkModeEnabled() {
|
// Refresh foreground colors
|
||||||
displayMode.colorScheme = .dark
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
MenuBarExtraManager.shared.restartMenuBarExtra()
|
self.selectedTab = .interface
|
||||||
} else {
|
}
|
||||||
displayMode.colorScheme = .light
|
if menubarEnabled{
|
||||||
MenuBarExtraManager.shared.restartMenuBarExtra()
|
MenuBarExtraManager.shared.restartMenuBarExtra()
|
||||||
}
|
}
|
||||||
case "Dark":
|
case "Dark":
|
||||||
displayMode.colorScheme = .dark
|
displayMode.colorScheme = .dark
|
||||||
MenuBarExtraManager.shared.restartMenuBarExtra()
|
if menubarEnabled{
|
||||||
|
MenuBarExtraManager.shared.restartMenuBarExtra()
|
||||||
|
}
|
||||||
case "Light":
|
case "Light":
|
||||||
displayMode.colorScheme = .light
|
displayMode.colorScheme = .light
|
||||||
MenuBarExtraManager.shared.restartMenuBarExtra()
|
if menubarEnabled{
|
||||||
|
MenuBarExtraManager.shared.restartMenuBarExtra()
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -148,6 +161,7 @@ struct InterfaceSettingsTab: View {
|
|||||||
if mini {
|
if mini {
|
||||||
appState.currentView = miniView ? .apps : .empty
|
appState.currentView = miniView ? .apps : .empty
|
||||||
showPopover = false
|
showPopover = false
|
||||||
|
findAndHideWindows(named: ["Pearcleaner"])
|
||||||
windowSettings.newWindow {
|
windowSettings.newWindow {
|
||||||
MiniMode(search: $search, showPopover: $showPopover)
|
MiniMode(search: $search, showPopover: $showPopover)
|
||||||
.environmentObject(locations)
|
.environmentObject(locations)
|
||||||
@@ -164,6 +178,7 @@ struct InterfaceSettingsTab: View {
|
|||||||
} else {
|
} else {
|
||||||
appState.currentView = .files
|
appState.currentView = .files
|
||||||
}
|
}
|
||||||
|
findAndHideWindows(named: ["Pearcleaner"])
|
||||||
windowSettings.newWindow {
|
windowSettings.newWindow {
|
||||||
RegularMode(search: $search, showPopover: $showPopover)
|
RegularMode(search: $search, showPopover: $showPopover)
|
||||||
.environmentObject(locations)
|
.environmentObject(locations)
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ struct SettingsView: View {
|
|||||||
@Binding var search: String
|
@Binding var search: String
|
||||||
@Binding var showFeature: Bool
|
@Binding var showFeature: Bool
|
||||||
@AppStorage("settings.general.glass") private var glass: Bool = true
|
@AppStorage("settings.general.glass") private var glass: Bool = true
|
||||||
|
@AppStorage("settings.general.selectedTab") private var selectedTab: CurrentTabView = .general
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
||||||
TabView() {
|
TabView(selection: $selectedTab) {
|
||||||
GeneralSettingsTab(showPopover: $showPopover, search: $search)
|
GeneralSettingsTab(showPopover: $showPopover, search: $search)
|
||||||
.tabItem {
|
.tabItem {
|
||||||
Label(CurrentTabView.general.title, systemImage: "gear")
|
Label(CurrentTabView.general.title, systemImage: "gear")
|
||||||
@@ -47,13 +48,11 @@ struct SettingsView: View {
|
|||||||
Label(CurrentTabView.about.title, systemImage: "info.circle")
|
Label(CurrentTabView.about.title, systemImage: "info.circle")
|
||||||
}
|
}
|
||||||
.tag(CurrentTabView.about)
|
.tag(CurrentTabView.about)
|
||||||
|
|
||||||
}
|
}
|
||||||
.background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil)
|
.background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,15 +17,22 @@ struct FilesView: View {
|
|||||||
@AppStorage("settings.general.instant") var instantSearch: Bool = true
|
@AppStorage("settings.general.instant") var instantSearch: Bool = true
|
||||||
@AppStorage("settings.general.brew") private var brew: Bool = false
|
@AppStorage("settings.general.brew") private var brew: Bool = false
|
||||||
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
|
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
|
||||||
|
@AppStorage("settings.general.selectedSort") var selectedSortAlpha: Bool = true
|
||||||
@Environment(\.colorScheme) var colorScheme
|
@Environment(\.colorScheme) var colorScheme
|
||||||
@Binding var showPopover: Bool
|
@Binding var showPopover: Bool
|
||||||
@Binding var search: String
|
@Binding var search: String
|
||||||
var regularWin: Bool
|
var regularWin: Bool
|
||||||
@State private var selectedOption = "Default"
|
// @State private var selectedOption = "Alpha"
|
||||||
@State private var elapsedTime = 0
|
@State private var elapsedTime = 0
|
||||||
@State private var timer: Timer? = nil
|
@State private var timer: Timer? = nil
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
||||||
|
|
||||||
|
let totalSelectedSize = appState.selectedItems.reduce(Int64(0)) { (result, url) in
|
||||||
|
result + (appState.appInfo.fileSize[url] ?? 0)
|
||||||
|
}
|
||||||
|
|
||||||
VStack(alignment: .center) {
|
VStack(alignment: .center) {
|
||||||
if appState.showProgress {
|
if appState.showProgress {
|
||||||
VStack {
|
VStack {
|
||||||
@@ -131,7 +138,9 @@ struct FilesView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Text("\(appState.appInfo.bundleIdentifier)").font(.title3)
|
Text("\(appState.appInfo.bundleIdentifier)")
|
||||||
|
.lineLimit(1)
|
||||||
|
.font(.title3)
|
||||||
.foregroundStyle((.gray.opacity(0.8)))
|
.foregroundStyle((.gray.opacity(0.8)))
|
||||||
}
|
}
|
||||||
.padding(.leading)
|
.padding(.leading)
|
||||||
@@ -140,7 +149,8 @@ struct FilesView: View {
|
|||||||
|
|
||||||
VStack(alignment: .trailing, spacing: 5) {
|
VStack(alignment: .trailing, spacing: 5) {
|
||||||
Text("\(formatByte(size: appState.appInfo.totalSize))").font(.title).fontWeight(.bold)
|
Text("\(formatByte(size: appState.appInfo.totalSize))").font(.title).fontWeight(.bold)
|
||||||
Text("\(appState.appInfo.fileSize.count > 1 ? "\(appState.appInfo.fileSize.count) items" : "\(appState.appInfo.fileSize.count) item")").font(.callout).underline().foregroundStyle((.gray.opacity(0.8)))
|
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((.gray.opacity(0.8)))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -205,9 +215,10 @@ struct FilesView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Button("") {
|
Button("") {
|
||||||
selectedOption = selectedOption == "Default" ? "Size" : "Default"
|
selectedSortAlpha.toggle()
|
||||||
|
// selectedOption = selectedOption == "Alpha" ? "Size" : "Alpha"
|
||||||
}
|
}
|
||||||
.buttonStyle(SimpleButtonStyle(icon: selectedOption == "Default" ? "textformat.abc" : "textformat.123", help: selectedOption == "Default" ? "Sorted alphabetically" : "Sorted by size", color: Color("mode")))
|
.buttonStyle(SimpleButtonStyle(icon: selectedSortAlpha ? "textformat.abc" : "textformat.123", help: selectedSortAlpha ? "Sorted alphabetically" : "Sorted by size", color: Color("mode")))
|
||||||
|
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
@@ -237,19 +248,20 @@ struct FilesView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let sort = selectedOption == "Default" ? sortedFilesAlpha : sortedFilesSize
|
let sort = selectedSortAlpha ? sortedFilesAlpha : sortedFilesSize
|
||||||
|
|
||||||
ForEach(sort, id: \.self) { path in
|
ForEach(Array(sort.enumerated()), id: \.element) { index, path in
|
||||||
if let fileSize = appState.appInfo.fileSize[path], let fileIcon = appState.appInfo.fileIcon[path] {
|
if let fileSize = appState.appInfo.fileSize[path], let fileIcon = appState.appInfo.fileIcon[path] {
|
||||||
let iconImage = fileIcon.map(Image.init(nsImage:))
|
let iconImage = fileIcon.map(Image.init(nsImage:))
|
||||||
VStack {
|
VStack {
|
||||||
FileDetailsItem(size: fileSize, icon: iconImage, path: path)
|
FileDetailsItem(size: fileSize, icon: iconImage, path: path)
|
||||||
if path != appState.appInfo.files.last {
|
if index < sort.count - 1 {
|
||||||
Divider().padding(.leading, 40).opacity(0.5)
|
Divider().padding(.leading, 40).opacity(0.5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
@@ -261,97 +273,64 @@ struct FilesView: View {
|
|||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if !appState.selectedItems.isEmpty {
|
Button("\(formatByte(size: totalSelectedSize))") {
|
||||||
Button("Uninstall") {
|
Task {
|
||||||
Task {
|
updateOnMain {
|
||||||
updateOnMain {
|
search = ""
|
||||||
search = ""
|
if !regularWin {
|
||||||
if !regularWin {
|
appState.currentView = .apps
|
||||||
appState.currentView = .apps
|
showPopover = false
|
||||||
|
} else {
|
||||||
|
appState.currentView = .empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let selectedItemsArray = Array(appState.selectedItems)
|
||||||
|
|
||||||
|
killApp(appId: appState.appInfo.bundleIdentifier) {
|
||||||
|
moveFilesToTrash(at: selectedItemsArray) {
|
||||||
|
withAnimation {
|
||||||
showPopover = false
|
showPopover = false
|
||||||
|
updateOnMain {
|
||||||
|
appState.currentView = mini ? .apps : .empty
|
||||||
|
appState.isReminderVisible.toggle()
|
||||||
|
}
|
||||||
|
if sentinel {
|
||||||
|
launchctl(load: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Brew cleanup if enabled
|
||||||
|
if brew {
|
||||||
|
caskCleanup(app: appState.appInfo.appName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove app from app list if main app bundle is removed
|
||||||
|
if selectedItemsArray.contains(where: { $0.absoluteString == appState.appInfo.path.absoluteString }) {
|
||||||
|
removeApp(appState: appState, withId: appState.appInfo.id)
|
||||||
} else {
|
} else {
|
||||||
appState.currentView = .empty
|
// Add deleted appInfo object to trashed array
|
||||||
|
appState.appInfo.files = []
|
||||||
|
appState.appInfo.fileSize = [:]
|
||||||
|
appState.trashedFiles.append(appState.appInfo)
|
||||||
|
|
||||||
|
// Clear out appInfoStore object
|
||||||
|
if let index = appState.appInfoStore.firstIndex(where: { $0.path == appState.appInfo.path }) {
|
||||||
|
appState.appInfoStore[index] = .empty
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
appState.appInfo = AppInfo.empty
|
||||||
}
|
}
|
||||||
let selectedItemsArray = Array(appState.selectedItems)
|
|
||||||
|
|
||||||
// Delete all folders above app bundle between /Applications and app bundle file
|
|
||||||
|
|
||||||
// if let url = URL(string: appState.appInfo.path.absoluteString) {
|
|
||||||
// var parentURL = url.deletingLastPathComponent() // Immediate parent of the .app bundle
|
|
||||||
//
|
|
||||||
// // Traverse up the path components until just below /Applications or ~/Applications
|
|
||||||
// while parentURL.pathComponents.count > 2 && parentURL.path.contains("Applications") && parentURL.deletingLastPathComponent().path != "/Applications" && parentURL.deletingLastPathComponent().path != "\(home)/Applications" {
|
|
||||||
// parentURL = parentURL.deletingLastPathComponent()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Now, parentURL should be the directory just below /Applications or ~/Applications
|
|
||||||
// if parentURL.path != "/Applications" && parentURL.path != "\(home)/Applications" {
|
|
||||||
// selectedItemsArray.insert(parentURL, at: 0) // Add the correct parent directory to the array
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Delete folder only 1 up from app bundle between /Applications and app bundle file
|
|
||||||
|
|
||||||
// if let url = URL(string: appState.appInfo.path.absoluteString) {
|
|
||||||
// let appFolderURL = appState.appInfo.path.absoluteString.contains("Wrapper") ? url.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent() : url.deletingLastPathComponent() // Get the immediate parent directory of regular and wrapped apps
|
|
||||||
//
|
|
||||||
// if appFolderURL.path == "/Applications" || appFolderURL.path == "\(home)/Applications" {
|
|
||||||
// // Do nothing, skip insertion
|
|
||||||
// } else if appFolderURL.pathComponents.count > 2 && appFolderURL.path.contains("Applications") {
|
|
||||||
// // Insert into selectedItemsArray only if there is an intermediary folder
|
|
||||||
// selectedItemsArray.insert(appFolderURL, at: 0)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
killApp(appId: appState.appInfo.bundleIdentifier) {
|
|
||||||
moveFilesToTrash(at: selectedItemsArray) {
|
|
||||||
withAnimation {
|
|
||||||
showPopover = false
|
|
||||||
updateOnMain {
|
|
||||||
appState.currentView = mini ? .apps : .empty
|
|
||||||
appState.isReminderVisible.toggle()
|
|
||||||
}
|
|
||||||
if sentinel {
|
|
||||||
launchctl(load: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Brew cleanup if enabled
|
|
||||||
if brew {
|
|
||||||
caskCleanup(app: appState.appInfo.appName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove app from app list if main app bundle is removed
|
|
||||||
if selectedItemsArray.contains(where: { $0.absoluteString == appState.appInfo.path.absoluteString }) {
|
|
||||||
removeApp(appState: appState, withId: appState.appInfo.id)
|
|
||||||
} else {
|
|
||||||
// Add deleted appInfo object to trashed array
|
|
||||||
appState.appInfo.files = []
|
|
||||||
appState.appInfo.fileSize = [:]
|
|
||||||
appState.trashedFiles.append(appState.appInfo)
|
|
||||||
|
|
||||||
// Clear out appInfoStore object
|
|
||||||
if let index = appState.appInfoStore.firstIndex(where: { $0.path == appState.appInfo.path }) {
|
|
||||||
appState.appInfoStore[index] = .empty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
appState.appInfo = AppInfo.empty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
.buttonStyle(NavButtonBottomBarStyle(image: "trash.fill", help: "Uninstall"))
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Text("No files selected to remove").font(.title).foregroundStyle(Color("mode")).opacity(0.2)
|
|
||||||
.padding(5)
|
|
||||||
}
|
}
|
||||||
|
.buttonStyle(UninstallButton(isEnabled: !appState.selectedItems.isEmpty))
|
||||||
|
.disabled(appState.selectedItems.isEmpty)
|
||||||
|
.padding(.top)
|
||||||
|
|
||||||
|
|
||||||
Spacer()
|
// Spacer()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ struct RegularMode: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// let slate = Color(.sRGB, red: 0.188143, green: 0.208556, blue: 0.262679, opacity: 1)
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
||||||
@@ -103,6 +104,7 @@ struct RegularMode: View {
|
|||||||
}
|
}
|
||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
}
|
}
|
||||||
|
// .background(slate)
|
||||||
// .onHover { hovering in
|
// .onHover { hovering in
|
||||||
// withAnimation(Animation.easeInOut(duration: 0.4)) {
|
// withAnimation(Animation.easeInOut(duration: 0.4)) {
|
||||||
// isHovering = hovering
|
// isHovering = hovering
|
||||||
|
|||||||
@@ -16,24 +16,29 @@ struct ZombieView: View {
|
|||||||
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
|
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
|
||||||
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
|
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
|
||||||
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
|
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
|
||||||
|
@AppStorage("settings.general.selectedSort") var selectedSortAlpha: Bool = true
|
||||||
@Environment(\.colorScheme) var colorScheme
|
@Environment(\.colorScheme) var colorScheme
|
||||||
@Binding var showPopover: Bool
|
@Binding var showPopover: Bool
|
||||||
@Binding var search: String
|
@Binding var search: String
|
||||||
@State private var searchZ: String = ""
|
@State private var searchZ: String = ""
|
||||||
@State private var selectedOption = "Default"
|
// @State private var selectedOption = "Alpha"
|
||||||
var regularWin: Bool
|
var regularWin: Bool
|
||||||
@State private var elapsedTime = 0
|
@State private var elapsedTime = 0
|
||||||
@State private var timer: Timer? = nil
|
@State private var timer: Timer? = nil
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
||||||
|
let totalSelectedZombieSize = appState.selectedZombieItems.reduce(Int64(0)) { (result, url) in
|
||||||
|
result + (appState.zombieFile.fileSize[url] ?? 0)
|
||||||
|
}
|
||||||
|
|
||||||
let filteredAndSortedFiles: ([URL], Int64) = {
|
let filteredAndSortedFiles: ([URL], Int64) = {
|
||||||
let filteredFiles = appState.zombieFile.fileSize.filter { (url, _) in
|
let filteredFiles = appState.zombieFile.fileSize.filter { (url, _) in
|
||||||
searchZ.isEmpty || url.lastPathComponent.localizedCaseInsensitiveContains(searchZ)
|
searchZ.isEmpty || url.lastPathComponent.localizedCaseInsensitiveContains(searchZ)
|
||||||
}
|
}
|
||||||
|
|
||||||
let sortedFilteredFiles = filteredFiles.sorted(by: {
|
let sortedFilteredFiles = filteredFiles.sorted(by: {
|
||||||
if selectedOption == "Default" {
|
if selectedSortAlpha {
|
||||||
return $0.key.lastPathComponent.pearFormat() < $1.key.lastPathComponent.pearFormat()
|
return $0.key.lastPathComponent.pearFormat() < $1.key.lastPathComponent.pearFormat()
|
||||||
} else {
|
} else {
|
||||||
return $0.value > $1.value
|
return $0.value > $1.value
|
||||||
@@ -148,7 +153,9 @@ struct ZombieView: View {
|
|||||||
|
|
||||||
VStack(alignment: .trailing, spacing: 5) {
|
VStack(alignment: .trailing, spacing: 5) {
|
||||||
Text("\(formatByte(size: filteredAndSortedFiles.1))").font(.title).fontWeight(.bold)
|
Text("\(formatByte(size: filteredAndSortedFiles.1))").font(.title).fontWeight(.bold)
|
||||||
Text("\(filteredAndSortedFiles.0.count == 1 ? "\(filteredAndSortedFiles.0.count) item" : "\(filteredAndSortedFiles.0.count) items")").font(.callout).underline().foregroundStyle((.gray.opacity(0.8)))
|
|
||||||
|
Text("\(appState.zombieFile.fileSize.count == 1 ? "\(appState.selectedZombieItems.count) / \(appState.zombieFile.fileSize.count) item" : "\(appState.selectedZombieItems.count) / \(appState.zombieFile.fileSize.count) items")")
|
||||||
|
.font(.callout).foregroundStyle((.gray.opacity(0.8)))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -158,10 +165,6 @@ struct ZombieView: View {
|
|||||||
.padding(.top, 0)
|
.padding(.top, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchBarMiniBottom(search: $searchZ)
|
|
||||||
// .padding(.top)
|
|
||||||
// .padding(.horizontal)
|
|
||||||
|
|
||||||
// Item selection and sorting toolbar
|
// Item selection and sorting toolbar
|
||||||
HStack {
|
HStack {
|
||||||
Toggle("", isOn: Binding(
|
Toggle("", isOn: Binding(
|
||||||
@@ -177,12 +180,11 @@ struct ZombieView: View {
|
|||||||
// .padding(.top)
|
// .padding(.top)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
|
|
||||||
// Spacer()
|
|
||||||
|
|
||||||
Button("") {
|
Button("") {
|
||||||
selectedOption = selectedOption == "Default" ? "Size" : "Default"
|
selectedSortAlpha.toggle()
|
||||||
}
|
}
|
||||||
.buttonStyle(SimpleButtonStyle(icon: selectedOption == "Default" ? "textformat.abc" : "textformat.123", help: selectedOption == "Default" ? "Sorted alphabetically" : "Sorted by size", color: Color("mode")))
|
.buttonStyle(SimpleButtonStyle(icon: selectedSortAlpha ? "textformat.abc" : "textformat.123", help: selectedSortAlpha ? "Sorted alphabetically" : "Sorted by size", color: Color("mode")))
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -193,22 +195,22 @@ struct ZombieView: View {
|
|||||||
Divider()
|
Divider()
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ScrollView() {
|
ScrollView() {
|
||||||
LazyVStack {
|
LazyVStack {
|
||||||
ForEach(filteredAndSortedFiles.0, id: \.self) { file in
|
ForEach(Array(filteredAndSortedFiles.0.enumerated()), id: \.element) { index, file in
|
||||||
if let fileSize = appState.zombieFile.fileSize[file], let fileIcon = appState.zombieFile.fileIcon[file] {
|
if let fileSize = appState.zombieFile.fileSize[file], let fileIcon = appState.zombieFile.fileIcon[file] {
|
||||||
let iconImage = fileIcon.map(Image.init(nsImage:))
|
let iconImage = fileIcon.map(Image.init(nsImage:))
|
||||||
|
VStack {
|
||||||
|
ZombieFileDetailsItem(size: fileSize, icon: iconImage, path: file)
|
||||||
|
.padding(.trailing)
|
||||||
|
|
||||||
ZombieFileDetailsItem(size: fileSize, icon: iconImage, path: file)
|
if index < filteredAndSortedFiles.0.count - 1 {
|
||||||
.padding(.trailing)
|
Divider().padding(.leading, 40).opacity(0.5)
|
||||||
|
}
|
||||||
if file != appState.zombieFile.fileSize.keys.sorted(by: { $0.absoluteString < $1.absoluteString }).last {
|
|
||||||
Divider().padding(.leading, 40).opacity(0.5)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
@@ -217,26 +219,9 @@ struct ZombieView: View {
|
|||||||
|
|
||||||
HStack() {
|
HStack() {
|
||||||
|
|
||||||
// Picker("", selection: Binding(
|
|
||||||
// get: { appState.selectedZombieItems.count == appState.zombieFile.fileSize.count ? true : false },
|
|
||||||
// set: { newValue in
|
|
||||||
// updateOnMain {
|
|
||||||
// appState.selectedZombieItems = newValue ? Set(appState.zombieFile.fileSize.keys) : []
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// )) {
|
|
||||||
// Image(systemName: "checkmark.square").tag(true)
|
|
||||||
// Image(systemName: "square").tag(false)
|
|
||||||
// }
|
|
||||||
// .pickerStyle(SegmentedPickerStyle())
|
|
||||||
// .frame(width: 100)
|
|
||||||
// .offset(x: -8)
|
|
||||||
// .help("Item Selection")
|
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if !appState.selectedZombieItems.isEmpty {
|
Button("\(formatByte(size: totalSelectedZombieSize))") {
|
||||||
Button("Remove") {
|
|
||||||
Task {
|
Task {
|
||||||
updateOnMain {
|
updateOnMain {
|
||||||
appState.zombieFile = .empty
|
appState.zombieFile = .empty
|
||||||
@@ -265,21 +250,13 @@ struct ZombieView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
.buttonStyle(NavButtonBottomBarStyle(image: "trash.fill", help: "Remove"))
|
.buttonStyle(UninstallButton(isEnabled: !appState.selectedZombieItems.isEmpty))
|
||||||
} else {
|
.disabled(appState.selectedZombieItems.isEmpty)
|
||||||
Text("No files selected to remove").font(.title).foregroundStyle(Color("mode")).opacity(0.2)
|
.padding(.top)
|
||||||
.padding(5)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
// Picker("", selection: $selectedOption) {
|
// Spacer()
|
||||||
// Image(systemName: "textformat.abc").tag("Default")
|
|
||||||
// Image(systemName: "number").tag("Size")
|
|
||||||
// }
|
|
||||||
// .pickerStyle(SegmentedPickerStyle())
|
|
||||||
// .frame(width: 100)
|
|
||||||
// .help("Sorting alphabetically or by size")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user