3.0.2 fixes

This commit is contained in:
Alin
2024-03-04 15:32:40 -07:00
parent 7895996118
commit 583f0d40a5
18 changed files with 346 additions and 299 deletions
+17 -12
View File
@@ -8,7 +8,8 @@
import SwiftUI
struct AppCommands: Commands {
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
let appState: AppState
let locations: Locations
@@ -32,18 +33,20 @@ struct AppCommands: Commands {
Button {
withAnimation(.easeInOut(duration: 0.5)) {
// Refresh Apps list
appState.reload.toggle()
let sortedApps = getSortedApps()
updateOnMain {
appState.reload.toggle()
}
let sortedApps = getSortedApps()
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
appState.sortedApps.userApps = []
appState.sortedApps.systemApps = []
appState.sortedApps.userApps = sortedApps.userApps
appState.sortedApps.systemApps = sortedApps.systemApps
}
Task(priority: .high){
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
}
updateOnMain {
if instantSearch {
Task(priority: .high){
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
}
}
appState.reload.toggle()
}
}
@@ -64,16 +67,18 @@ struct AppCommands: Commands {
// Edit Menu
CommandGroup(replacing: .undoRedo) {
Button
{
undoTrash(appState: appState) {
let sortedApps = getSortedApps()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
appState.sortedApps.userApps = []
appState.sortedApps.systemApps = []
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
appState.sortedApps.userApps = sortedApps.userApps
appState.sortedApps.systemApps = sortedApps.systemApps
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
// if instantSearch {
// loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
// }
}
}
} label: {
+12 -6
View File
@@ -321,7 +321,14 @@ func findPathsForApp(appInfo: AppInfo = .empty, appState: AppState, locations: L
var collection: [URL] = []
if let url = URL(string: appInfo.path.absoluteString) {
collection.insert(url, at: 0)
if !url.path.contains(".Trash") {
if url.path.contains("Wrapper") {
let modifiedUrl = url.deletingLastPathComponent().deletingLastPathComponent()
collection.insert(modifiedUrl, at: 0)
} else {
collection.insert(url, at: 0)
}
}
}
@@ -329,7 +336,6 @@ func findPathsForApp(appInfo: AppInfo = .empty, appState: AppState, locations: L
let dispatchGroup = DispatchGroup()
var bundleComponents = appInfo.bundleIdentifier.components(separatedBy: ".")
if let lastComponent = bundleComponents.last, let rangeOfDash = lastComponent.range(of: "-") {
// Remove the dash and everything after it
let updatedLastComponent = String(lastComponent[..<rangeOfDash.lowerBound])
bundleComponents[bundleComponents.count - 1] = updatedLastComponent
}
@@ -370,6 +376,7 @@ func findPathsForApp(appInfo: AppInfo = .empty, appState: AppState, locations: L
if collection.contains(itemURL) {
continue
}
// Catch web app plist files
if appInfo.webApp {
if itemL.contains(bundleIdentifierL) {
@@ -417,7 +424,6 @@ func findPathsForApp(appInfo: AppInfo = .empty, appState: AppState, locations: L
collection.append(contentsOf: groupContainers)
var sortedCollection = collection.sorted(by: { $0.absoluteString < $1.absoluteString })
// Calculate file details (sizes and icons)
var fileSize: [URL: Int64] = [:]
var fileIcon: [URL: NSImage?] = [:]
@@ -438,6 +444,7 @@ func findPathsForApp(appInfo: AppInfo = .empty, appState: AppState, locations: L
sortedCollection = []
}
}
// Save to appState
dispatchGroup.notify(queue: .main) {
@@ -593,7 +600,7 @@ func reversePathsSearch(appState: AppState, locations: Locations, completion: @e
let dispatchGroup = DispatchGroup()
let allPaths = appState.appInfoStore.flatMap { $0.files.map { $0.path.pearFormat() } }
let allNames = appState.appInfoStore.map { $0.appName.pearFormat() }
let skipped = ["apple", "comapple", "temporary", "btserver", "proapps", "scripteditor", "ilife", "livefsd", "siritoday", "addressbook", "animoji", "appstore", "askpermission", "callhistory", "clouddocs", "diskimages", "dock", "facetime", "fileprovider", "instruments", "knowledge", "mobilesync", "syncservices", "homeenergyd", "icloud", "icdd", "networkserviceproxy", "familycircle", "geoservices", "installation", "passkit", "sharedimagecache", "desktop", "mbuseragent", "swiftpm", "baseband", "coresimulator", "photoslegacyupgrade", "photosupgrade", "siritts", "ipod", "globalpreferences", "apmanalytics", "apmexperiment", "avatarcache", "byhost", "contextstoreagent", "mobilemeaccounts", "intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd", "clang", "audiocomponent", "csexattrcryptoservice", "livetranscriptionagent", "sandobxhelper", "statuskitagent", "betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed", "heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd", "metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd", "talagent", "watchlistd", "apptranslocation", "xcrun", "ds_store", "caches", "crashreporter"] // Skip system folders
let skipped = ["apple", "comapple", "temporary", "btserver", "proapps", "scripteditor", "ilife", "livefsd", "siritoday", "addressbook", "animoji", "appstore", "askpermission", "callhistory", "clouddocs", "diskimages", "dock", "facetime", "fileprovider", "instruments", "knowledge", "mobilesync", "syncservices", "homeenergyd", "icloud", "icdd", "networkserviceproxy", "familycircle", "geoservices", "installation", "passkit", "sharedimagecache", "desktop", "mbuseragent", "swiftpm", "baseband", "coresimulator", "photoslegacyupgrade", "photosupgrade", "siritts", "ipod", "globalpreferences", "apmanalytics", "apmexperiment", "avatarcache", "byhost", "contextstoreagent", "mobilemeaccounts", "intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd", "clang", "audiocomponent", "csexattrcryptoservice", "livetranscriptionagent", "sandobxhelper", "statuskitagent", "betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed", "heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd", "metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd", "talagent", "watchlistd", "apptranslocation", "xcrun", "ds_store", "caches", "crashreporter", "trash"] // Skip system folders
// Skip locations that might not exist
@@ -654,7 +661,6 @@ func reversePathsSearch(appState: AppState, locations: Locations, completion: @e
updateOnMain {
updatedZombieFile.fileSize = fileSize
updatedZombieFile.fileIcon = fileIcon
// appState.selectedZombieItems = Set(sortedCollection)
appState.zombieFile = updatedZombieFile
appState.showProgress = false
}
@@ -683,7 +689,7 @@ func moveFilesToTrash(at fileURLs: [URL], completion: @escaping () -> Void = {})
}
}
}
if !filesSudo.isEmpty {
// Remove socket files with rm
let filesSudoPaths = filesSudo.map { $0.path }
+34
View File
@@ -504,6 +504,40 @@ struct WindowActionButton: ButtonStyle {
}
struct SimpleButtonBrightStyle: ButtonStyle {
@State private var hovered = false
let icon: String
let help: String
let color: Color
let shield: Bool?
init(icon: String, help: String, color: Color, shield: Bool? = nil) {
self.icon = icon
self.help = help
self.color = color
self.shield = shield
}
func makeBody(configuration: Self.Configuration) -> some View {
HStack {
Image(systemName: icon)
.resizable()
.scaledToFit()
.frame(width: 20)
.foregroundColor(hovered ? color.opacity(0.5) : color)
}
.padding(5)
.onHover { hovering in
withAnimation() {
hovered = hovering
}
}
.scaleEffect(configuration.isPressed ? 0.95 : 1)
.help(help)
}
}
struct FilesViewActionButton: ButtonStyle {
enum UserAction {
+4 -2
View File
@@ -427,7 +427,7 @@ func ensureApplicationSupportFolderExists(appState: AppState) {
let fileManager = FileManager.default
let supportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("Pearcleaner")
// Check to make sure Application Support/Support Admin folder exists
// Check to make sure Application Support/Pearcleaner folder exists
if !fileManager.fileExists(atPath: supportURL.path) {
try! fileManager.createDirectory(at: supportURL, withIntermediateDirectories: true)
printOS("Created Application Support/Pearcleaner folder")
@@ -463,7 +463,7 @@ func writeLog(string: String) {
// --- Load Plist file with launchctl ---
func launchctl(load: Bool) {
func launchctl(load: Bool, completion: @escaping () -> Void = {}) {
let cmd = load ? "load" : "unload"
if let plistPath = Bundle.main.path(forResource: "com.alienator88.PearcleanerSentinel", ofType: "plist") {
var plistContent = try! String(contentsOfFile: plistPath)
@@ -491,6 +491,8 @@ func launchctl(load: Bool) {
task.launch()
completion()
}
}
+2 -5
View File
@@ -29,8 +29,6 @@ struct PearcleanerApp: App {
var body: some Scene {
WindowGroup {
Group {
ZStack() {
@@ -53,7 +51,7 @@ struct PearcleanerApp: App {
}
.environmentObject(appState)
.preferredColorScheme(displayMode.colorScheme)
.alert(isPresented: $appState.showAlert) { presentAlert(appState: appState) }
// .alert(isPresented: $appState.showAlert) { presentAlert(appState: appState) }
.handlesExternalEvents(preferring: Set(arrayLiteral: "pear"), allowing: Set(arrayLiteral: "*"))
.onOpenURL(perform: { url in
let deeplinkManager = DeeplinkManager(showPopover: $showPopover)
@@ -101,8 +99,7 @@ struct PearcleanerApp: App {
if instantSearch {
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
}
#if !DEBUG
Task {
@@ -5,9 +5,9 @@
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0xFF",
"green" : "0xFF",
"red" : "0xFF"
"blue" : "0x50",
"green" : "0xC3",
"red" : "0x79"
}
},
"idiom" : "universal"
@@ -23,9 +23,9 @@
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0xFF",
"green" : "0xFF",
"red" : "0xFF"
"blue" : "0x50",
"green" : "0xC3",
"red" : "0x79"
}
},
"idiom" : "universal"
+1 -1
View File
@@ -48,7 +48,7 @@ struct GeneralSettingsTab: View {
.font(.callout)
.foregroundStyle(.gray)
}
InfoButton(text: "When transparent material is enabled, sticky section headers (User/System) in app list are disabled to keep from showing app name text overlayed under the section header text with no background to separate the two.", color: nil)
// InfoButton(text: "When transparent material is enabled, sticky section headers (User/System) in app list are disabled to keep from showing app name text overlayed under the section header text with no background to separate the two.", color: nil)
Spacer()
Toggle(isOn: $glass, label: {
})
+1 -1
View File
@@ -24,7 +24,7 @@ struct Asset: Codable {
extension Release {
var modifiedBody: String {
return body.replacingOccurrences(of: "- [x]", with: "􀆊").replacingOccurrences(of: "###", with: "")
return body.replacingOccurrences(of: "- [x]", with: ">").replacingOccurrences(of: "###", with: "")
}
}
+2 -46
View File
@@ -64,53 +64,9 @@ struct AppListView: View {
}
}
.padding(.top, 20)
// .padding(.bottom)
ScrollView {
LazyVStack(alignment: .leading, spacing: 0, pinnedViews: [glass ? [] : .sectionHeaders]) {
if filteredUserApps.count > 0 {
// VStack {
// Header(title: "User", count: filteredUserApps.count, showPopover: $showPopover)
ForEach(filteredUserApps, id: \.self) { appInfo in
Section(header: Header(title: "User", count: filteredUserApps.count, showPopover: $showPopover)) {
AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo)
.padding(.vertical,5)
if appInfo != filteredUserApps.last {
Divider().padding(.horizontal, 5)
}
}
// AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo)
// if appInfo != filteredUserApps.last {
// Divider().padding(.horizontal, 5)
// }
}
// }
}
if filteredSystemApps.count > 0 {
// VStack {
// Header(title: "System", count: filteredSystemApps.count, showPopover: $showPopover)
Section(header: Header(title: "System", count: filteredSystemApps.count, showPopover: $showPopover)) {
ForEach(filteredSystemApps, id: \.self) { appInfo in
AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo)
.padding(.vertical,5)
if appInfo != filteredSystemApps.last {
Divider().padding(.horizontal, 5)
}
}
}
// }
}
}
.padding(.horizontal)
}
.scrollIndicators(.never)
AppsListView(search: $search, showPopover: $showPopover, filteredUserApps: filteredUserApps, filteredSystemApps: filteredSystemApps)
}
.frame(width: sidebarWidth)
@@ -267,6 +223,6 @@ struct Header: View {
}
.padding(.vertical, 9)
.padding(.leading, 6)
.background(!glass ? Color("windowBG") : .clear)
// .background(!glass ? Color("windowBG") : .clear)
}
}
+79
View File
@@ -0,0 +1,79 @@
//
// AppsListView.swift
// Pearcleaner
//
// Created by Alin Lupascu on 3/4/24.
//
import Foundation
import SwiftUI
struct AppsListView: View {
@Binding var search: String
@Binding var showPopover: Bool
var filteredUserApps: [AppInfo]
var filteredSystemApps: [AppInfo]
var body: some View {
ScrollView {
LazyVStack(alignment: .leading, spacing: 0, pinnedViews: [.sectionHeaders]) {
if filteredUserApps.count > 0 {
SectionView(title: "User", count: filteredUserApps.count, apps: filteredUserApps, search: $search, showPopover: $showPopover)
}
if filteredSystemApps.count > 0 {
SectionView(title: "System", count: filteredSystemApps.count, apps: filteredSystemApps, search: $search, showPopover: $showPopover)
}
}
.padding(.horizontal)
}
.scrollIndicators(.never)
}
}
struct SectionView: View {
var title: String
var count: Int
var apps: [AppInfo]
@Binding var search: String
@Binding var showPopover: Bool
var body: some View {
VStack(spacing: 0) {
Header(title: title, count: count, showPopover: $showPopover)
ForEach(apps, id: \.self) { appInfo in
AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo)
.padding(.vertical, 5)
if appInfo != apps.last {
Divider().padding(.horizontal, 5)
}
}
}
}
}
//List {
// Section {
// ForEach(filteredUserApps, id:\.self) { element in
// Text(element.appName)
// }
// .listRowBackground(Color.clear)
// } header: {
// Text("User Apps")
// }
//
// Section {
// ForEach(filteredSystemApps, id:\.self) { element in
// Text(element.appName)
// }
// .listRowBackground(Color.clear)
// } header: {
// Text("System Apps")
// }
//
//}
//.scrollContentBackground(.hidden)
//.background(.clear)
//.scrollIndicators(.never)
+31 -35
View File
@@ -14,6 +14,7 @@ struct FilesView: View {
@State private var showPop: Bool = false
@AppStorage("settings.general.mini") private var mini: Bool = false
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
@AppStorage("settings.general.instant") var instantSearch: Bool = true
@Environment(\.colorScheme) var colorScheme
@Binding var showPopover: Bool
@Binding var search: String
@@ -35,8 +36,24 @@ struct FilesView: View {
.frame(maxWidth: .infinity, maxHeight: .infinity)
.transition(.opacity)
} else {
VStack() {
// Titlebar
if mini {
HStack() {
Spacer()
Button("Close") {
updateOnMain {
appState.appInfo = AppInfo.empty
search = ""
appState.currentView = .apps
showPopover = false
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "x.circle.fill", help: "Close"))
}
.padding([.horizontal, .top], 5)
}
VStack(spacing: 0) {
// Main Group
HStack(alignment: .center) {
@@ -112,7 +129,7 @@ struct FilesView: View {
}
.padding(.horizontal, 20)
.padding(.top, 10)
.padding(.top, 0)
}
Divider()
@@ -153,8 +170,8 @@ struct FilesView: View {
}
}
)) {
Text("􀃲").tag(true)
Text("􀂒").tag(false)
Image(systemName: "checkmark.square").tag(true)
Image(systemName: "square").tag(false)
}
.pickerStyle(SegmentedPickerStyle())
.frame(width: 100)
@@ -163,20 +180,6 @@ struct FilesView: View {
Spacer()
if mini {
Button("Close") {
updateOnMain {
appState.appInfo = AppInfo.empty
search = ""
appState.currentView = .apps
showPopover = false
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "x.circle.fill", help: "Close"))
}
if !appState.selectedItems.isEmpty {
Button("Uninstall") {
Task {
@@ -191,13 +194,9 @@ struct FilesView: View {
}
}
var selectedItemsArray = Array(appState.selectedItems)
.filter { !$0.path.contains(".Trash") }
.map { path in
return path.path.contains("Wrapper") ? path.deletingLastPathComponent().deletingLastPathComponent() : path
}
if let url = URL(string: appState.appInfo.path.absoluteString) {
let appFolderURL = url.deletingLastPathComponent() // Get the immediate parent directory
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
@@ -207,7 +206,6 @@ struct FilesView: View {
}
}
killApp(appId: appState.appInfo.bundleIdentifier) {
moveFilesToTrash(at: selectedItemsArray) {
withAnimation {
@@ -226,7 +224,7 @@ struct FilesView: View {
.buttonStyle(NavButtonBottomBarStyle(image: "trash.fill", help: "Uninstall"))
} else {
Text("No files selected to clean").font(.title).foregroundStyle(Color("mode")).opacity(0.2)
Text("No files selected to remove").font(.title).foregroundStyle(Color("mode")).opacity(0.2)
.padding(5)
}
@@ -234,17 +232,17 @@ struct FilesView: View {
Spacer()
Picker("", selection: $selectedOption) {
Text("􀅐").tag("Default")
Text("􀆃").tag("Size")
Image(systemName: "textformat.abc").tag("Default")
Image(systemName: "number").tag("Size")
}
.pickerStyle(SegmentedPickerStyle())
.frame(width: 100)
.help("Sorting Selection")
.help("Sorting alphabetically or by size")
}
}
.transition(.opacity)
.padding(20)
.padding([.horizontal, .bottom], 20)
}
@@ -254,14 +252,12 @@ struct FilesView: View {
func refreshAppList(_ appInfo: AppInfo) {
showPopover = false
let sortedApps = getSortedApps()
updateOnMain {
appState.sortedApps.userApps = []
appState.sortedApps.systemApps = []
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
appState.sortedApps.userApps = sortedApps.userApps
appState.sortedApps.systemApps = sortedApps.systemApps
}
Task(priority: .high){
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
// if instantSearch {
// loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
// }
}
}
}
+1 -43
View File
@@ -186,49 +186,7 @@ struct MiniAppView: View {
} else {
VStack(alignment: .center) {
ScrollView {
LazyVStack(alignment: .leading, spacing: 0, pinnedViews: [glass ? [] : .sectionHeaders]) {
if filteredUserApps.count > 0 {
// VStack {
// Header(title: "User", count: filteredUserApps.count, showPopover: $showPopover)
Section(header: Header(title: "User", count: filteredUserApps.count, showPopover: $showPopover)) {
ForEach(filteredUserApps, id: \.self) { appInfo in
AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo)
// .padding(.vertical,5)
if appInfo != filteredUserApps.last {
Divider().padding(.horizontal, 5)
}
}
}
// }
}
if filteredSystemApps.count > 0 {
// VStack {
// Header(title: "System", count: filteredSystemApps.count, showPopover: $showPopover)
Section(header: Header(title: "System", count: filteredSystemApps.count, showPopover: $showPopover)) {
ForEach(filteredSystemApps, id: \.self) { appInfo in
AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo)
.padding(.vertical,5)
if appInfo != filteredSystemApps.last {
Divider().padding(.horizontal, 5)
}
}
}
// }
}
}
.padding(.horizontal)
}
.scrollIndicators(.never)
AppsListView(search: $search, showPopover: $showPopover, filteredUserApps: filteredUserApps, filteredSystemApps: filteredSystemApps)
if appState.currentView != .empty {
SearchBarMiniBottom(search: $search).background(.ultraThinMaterial)
+38 -57
View File
@@ -37,8 +37,38 @@ struct ZombieView: View {
.frame(maxWidth: .infinity, maxHeight: .infinity)
.transition(.opacity)
} else {
VStack() {
// Titlebar
if mini {
HStack(spacing: 0) {
Spacer()
Button("Rescan") {
updateOnMain {
appState.zombieFile = .empty
appState.showProgress.toggle()
if instantSearch {
reversePathsSearch(appState: appState, locations: locations)
} else {
loadAllPaths(allApps: appState.sortedApps.userApps + appState.sortedApps.systemApps, appState: appState, locations: locations, reverseAddon: true)
}
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
Button("Close") {
updateOnMain {
appState.appInfo = AppInfo.empty
search = ""
appState.currentView = .apps
showPopover = false
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "x.circle.fill", help: "Close"))
}
.padding([.horizontal, .top], 5)
}
VStack() {
// Main Group
HStack() {
@@ -74,7 +104,7 @@ struct ZombieView: View {
}
.padding(.horizontal, 20)
.padding(.top, 10)
.padding(.top, 0)
}
Divider()
@@ -116,8 +146,8 @@ struct ZombieView: View {
}
}
)) {
Text("􀃲").tag(true)
Text("􀂒").tag(false)
Image(systemName: "checkmark.square").tag(true)
Image(systemName: "square").tag(false)
}
.pickerStyle(SegmentedPickerStyle())
.frame(width: 100)
@@ -126,23 +156,6 @@ struct ZombieView: View {
Spacer()
if mini {
Button("Close") {
updateOnMain {
appState.appInfo = AppInfo.empty
search = ""
appState.currentView = .apps
showPopover = false
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "x.circle.fill", help: "Close"))
}
if !appState.selectedZombieItems.isEmpty {
Button("Remove") {
Task {
@@ -158,7 +171,6 @@ struct ZombieView: View {
}
let selectedItemsArray = Array(appState.selectedZombieItems)
.filter { !$0.path.contains(".Trash") }
killApp(appId: appState.appInfo.bundleIdentifier) {
moveFilesToTrash(at: selectedItemsArray) {
@@ -166,9 +178,6 @@ struct ZombieView: View {
showPopover = false
updateOnMain {
appState.isReminderVisible.toggle()
if sentinel {
launchctl(load: true)
}
}
}
}
@@ -183,53 +192,25 @@ struct ZombieView: View {
.padding(5)
}
Button("Rescan") {
updateOnMain {
appState.zombieFile = .empty
appState.showProgress.toggle()
if instantSearch {
reversePathsSearch(appState: appState, locations: locations)
} else {
loadAllPaths(allApps: appState.sortedApps.userApps + appState.sortedApps.systemApps, appState: appState, locations: locations, reverseAddon: true)
}
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
Spacer()
Picker("", selection: $selectedOption) {
Text("􀅐").tag("Default")
Text("􀆃").tag("Size")
Image(systemName: "textformat.abc").tag("Default")
Image(systemName: "number").tag("Size")
}
.pickerStyle(SegmentedPickerStyle())
.frame(width: 100)
.help("Sorting Selection")
.help("Sorting alphabetically or by size")
}
}
.transition(.opacity)
.padding(20)
.padding([.horizontal, .bottom], 20)
}
}
}
func refreshAppList(_ appInfo: AppInfo) {
showPopover = false
let sortedApps = getSortedApps()
updateOnMain {
appState.sortedApps.userApps = []
appState.sortedApps.systemApps = []
appState.sortedApps.userApps = sortedApps.userApps
appState.sortedApps.systemApps = sortedApps.systemApps
}
Task(priority: .high){
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
}
}
}
+6 -2
View File
@@ -27,7 +27,7 @@ class NewWin: NSWindowController {
let window = Self.makeWindow(width: width, height: height)
window.backgroundColor = NSColor.controlBackgroundColor
self.init(window: window)
// Using Visual Effect to make titlebar fully transparent
@@ -47,7 +47,7 @@ class NewWin: NSWindowController {
window.titleVisibility = .hidden
window.titlebarAppearsTransparent = true
window.center()
window.title = "Support Admin"
window.title = "Pearcleaner"
}
@@ -65,10 +65,12 @@ class NewWin: NSWindowController {
}
private func makeNewView(appState: AppState, newWin: NewWindow) -> some View {
switch newWin {
case .update:
return AnyView(UpdateView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.black.opacity(0.2))
.environmentObject(appState))
case .no_update:
return AnyView(NoUpdateView()
@@ -77,6 +79,8 @@ class NewWin: NSWindowController {
case .perm:
return AnyView(PermView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.black.opacity(0.2))
.environmentObject(appState))
}
}
+54 -24
View File
@@ -17,34 +17,69 @@ struct PermView: View {
Spacer()
Text("Attention")
Text("Pearcleaner Permissions")
.font(.title)
.bold()
.padding(.vertical)
.padding(.top, 5)
Spacer()
}
.background(.red)
Text("Pearcleaner requires the following permissions:")
.font(.title3)
.fontWeight(.semibold)
.padding(.vertical)
ScrollView {
Text(" - Full Disk Access permission to find and delete files in non-user locations.\n - Accessibility permission to delete files via Finder which allows for the Undo function.\n\nAdd Pearcleaner in both Privacy panes via the + or by dragging the app over the pane. If the app is already pre-populated in the list, just toggle On. Restart app when both permissions are granted.")
.font(.body)
.multilineTextAlignment(.leading)
.padding(.horizontal, 20)
Divider()
.padding(.bottom)
Spacer()
VStack(alignment: .leading, spacing: 20) {
HStack(alignment: .top, spacing: 20) {
Image(systemName: "externaldrive")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundStyle(.gray)
Text("Full Disk Access permission to find and delete files in system paths")
.font(.callout)
.foregroundStyle(.gray)
}
HStack(alignment: .top, spacing: 20) {
Image(systemName: "accessibility")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundStyle(.gray)
Text("Accessibility permission to delete files via Finder")
.font(.callout)
.foregroundStyle(.gray)
}
HStack(alignment: .top, spacing: 20) {
Image(systemName: "info.circle")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundStyle(.gray)
Text("Add Pearcleaner in both Privacy panes via the + or by dragging the app over the pane. If the app is already pre-populated in the list, just toggle On if neeeded. Restart app when both permissions are granted")
.font(.callout)
.foregroundStyle(.gray)
}
}
.frame(height: 130)
.padding(.horizontal)
.padding(.horizontal, 20)
Spacer()
HStack {
Button(action: {
relaunchApp(afterDelay: 1)
}) {
Text("Restart")
}
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.uturn.left.circle", help: "Restart", color: .red))
.padding()
Button(action: {
if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") {
NSWorkspace.shared.open(url)
@@ -52,7 +87,8 @@ struct PermView: View {
}) {
Text("Full Disk Access")
}
.buttonStyle(WindowActionButton(action: .accept))
.buttonStyle(SimpleButtonBrightStyle(icon: "externaldrive", help: "Full Disk", color: .accentColor))
.padding()
Button(action: {
@@ -62,16 +98,10 @@ struct PermView: View {
}) {
Text("Accessibility")
}
.buttonStyle(WindowActionButton(action: .accept))
.buttonStyle(SimpleButtonBrightStyle(icon: "accessibility", help: "Accessibility", color: .accentColor))
.padding()
Button(action: {
relaunchApp(afterDelay: 1)
}) {
Text("Restart")
}
.buttonStyle(WindowActionButton(action: .cancel))
.padding()
}
+28 -37
View File
@@ -13,7 +13,7 @@ struct UpdateView: View {
let isAppInAppsDir = isAppInApplicationsDir()
var body: some View {
VStack {
VStack(spacing: 5) {
HStack {
Spacer()
@@ -29,36 +29,28 @@ struct UpdateView: View {
Text("v\(appState.releases.first?.tag_name ?? "")")
.font(.title3)
.fontWeight(.semibold)
.foregroundStyle(Color.white.opacity(0.5))
.opacity(0.5)
.padding()
}
.background(.blue)
Spacer()
Divider()
.padding([.horizontal])
ScrollView {
Text("\(appState.releases.first?.body ?? "No release information")")
Text("\(appState.releases.first?.modifiedBody ?? "No release information")")
.font(.body)
.multilineTextAlignment(.leading)
.padding(.horizontal, 20)
.padding(20)
}
.frame(height: 160)
.padding()
Spacer()
if !isAppInAppsDir {
HStack {
Text("Pearcleaner not in /Applications directory. Updater could have permission issues updating files.")
.font(.footnote)
.foregroundStyle(.red)
.padding()
.lineLimit(2)
.fixedSize(horizontal: false, vertical: true)
}
Text("Please move Pearcleaner to the **Applications** folder before updating!")
.font(.callout)
.foregroundStyle(.red)
.padding(.top, 0)
}
VStack() {
@@ -73,7 +65,7 @@ struct UpdateView: View {
Button(action: NewWin.close) {
Text("Cancel")
}
.buttonStyle(WindowActionButton(action: .cancel))
.buttonStyle(SimpleButtonBrightStyle(icon: "x.circle", help: "Close", color: .red))
if appState.progressBar.1 != 1.0 {
Button(action: {
@@ -81,7 +73,7 @@ struct UpdateView: View {
}) {
Text("Update")
}
.buttonStyle(WindowActionButton(action: .accept))
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.down.circle", help: "Update", color: .accentColor))
} else {
Button(action: {
NewWin.close()
@@ -89,7 +81,7 @@ struct UpdateView: View {
}) {
Text("Restart")
}
.buttonStyle(WindowActionButton(action: .accept))
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.uturn.left.circle", help: "Restart", color: .accentColor))
}
}
@@ -99,7 +91,7 @@ struct UpdateView: View {
}
.padding(EdgeInsets(top: -25, leading: 0, bottom: 25, trailing: 0))
}
@@ -110,9 +102,9 @@ struct UpdateView: View {
struct NoUpdateView: View {
@EnvironmentObject var appState: AppState
var body: some View {
VStack {
VStack(spacing: 5) {
HStack {
Spacer()
@@ -128,19 +120,19 @@ struct NoUpdateView: View {
Text("v\(appState.releases.first?.tag_name ?? "")")
.font(.title3)
.fontWeight(.semibold)
.foregroundStyle(Color.white.opacity(0.5))
.opacity(0.5)
.padding()
}
.background(.blue)
Spacer()
Divider()
.padding([.horizontal])
Text("Pearcleaner is on the most current release available, but you may force a re-download of the same version below.")
.font(.body)
.lineLimit(3)
.multilineTextAlignment(.leading)
.padding(20)
.padding()
@@ -160,15 +152,15 @@ struct NoUpdateView: View {
}) {
Text("Force Update")
}
.buttonStyle(WindowActionButton(action: .cancel))
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.down.circle", help: "Force-Update", color: .red))
Button(action: {
NewWin.close()
}) {
Text("Okay")
}
.buttonStyle(WindowActionButton(action: .accept))
.buttonStyle(SimpleButtonBrightStyle(icon: "checkmark.circle", help: "Ok", color: .accentColor))
}
@@ -179,7 +171,7 @@ struct NoUpdateView: View {
}) {
Text("Restart")
}
.buttonStyle(WindowActionButton(action: .accept))
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.uturn.left.circle", help: "Restart", color: .accentColor))
}
}
.padding(.bottom)
@@ -190,7 +182,6 @@ struct NoUpdateView: View {
.padding(EdgeInsets(top: -25, leading: 0, bottom: 25, trailing: 0))
}