mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 09:38:51 +00:00
v1.6
This commit is contained in:
@@ -535,7 +535,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 = 6;
|
CURRENT_PROJECT_VERSION = 7;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = BK8443AXLU;
|
DEVELOPMENT_TEAM = BK8443AXLU;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -552,7 +552,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.5;
|
MARKETING_VERSION = 1.6;
|
||||||
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;
|
||||||
@@ -569,7 +569,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 = 6;
|
CURRENT_PROJECT_VERSION = 7;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = BK8443AXLU;
|
DEVELOPMENT_TEAM = BK8443AXLU;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -586,7 +586,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.5;
|
MARKETING_VERSION = 1.6;
|
||||||
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;
|
||||||
|
|||||||
@@ -79,17 +79,42 @@ struct LabeledDivider: View {
|
|||||||
struct AnimatedSearchStyle: TextFieldStyle {
|
struct AnimatedSearchStyle: TextFieldStyle {
|
||||||
@State private var isHovered = false
|
@State private var isHovered = false
|
||||||
@Binding var text: String
|
@Binding var text: String
|
||||||
|
@Binding var reload: Bool
|
||||||
|
@EnvironmentObject var appState: AppState
|
||||||
|
|
||||||
func _body(configuration: TextField<Self._Label>) -> some View {
|
func _body(configuration: TextField<Self._Label>) -> some View {
|
||||||
|
|
||||||
HStack(spacing: 5) {
|
HStack(spacing: 5) {
|
||||||
|
|
||||||
if isHovered || !text.isEmpty {
|
if isHovered || !text.isEmpty {
|
||||||
|
|
||||||
configuration
|
configuration
|
||||||
.frame(width: 160)
|
// .frame(minWidth: 130)
|
||||||
.frame(height: 15)
|
.frame(height: 15)
|
||||||
.font(.title3)
|
.font(.title3)
|
||||||
.textFieldStyle(PlainTextFieldStyle())
|
.textFieldStyle(PlainTextFieldStyle())
|
||||||
|
.padding(.leading, 50)
|
||||||
|
|
||||||
|
|
||||||
|
Image(systemName: "arrow.triangle.2.circlepath")
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(width: 15, height: 15)
|
||||||
|
.padding(.trailing, 5)
|
||||||
|
.foregroundStyle(isHovered ? Color("mode").opacity(0.8) : Color("mode").opacity(0.5))
|
||||||
|
.onTapGesture {
|
||||||
|
withAnimation {
|
||||||
|
// Refresh Apps list
|
||||||
|
reload.toggle()
|
||||||
|
let sortedApps = getSortedApps()
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||||
|
appState.sortedApps.userApps = sortedApps.userApps
|
||||||
|
appState.sortedApps.systemApps = sortedApps.systemApps
|
||||||
|
reload.toggle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Image(systemName: "magnifyingglass")
|
Image(systemName: "magnifyingglass")
|
||||||
@@ -106,6 +131,7 @@ struct AnimatedSearchStyle: TextFieldStyle {
|
|||||||
RoundedRectangle(cornerRadius: 6)
|
RoundedRectangle(cornerRadius: 6)
|
||||||
.strokeBorder(Color("mode").opacity(0.4), lineWidth: 1)
|
.strokeBorder(Color("mode").opacity(0.4), lineWidth: 1)
|
||||||
.allowsHitTesting(false)
|
.allowsHitTesting(false)
|
||||||
|
.padding(.leading, 50)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -152,14 +178,17 @@ struct SimpleSearchStyle: TextFieldStyle {
|
|||||||
.padding(.trailing, 5)
|
.padding(.trailing, 5)
|
||||||
.foregroundStyle(isHovered ? Color("mode").opacity(0.8) : Color("mode").opacity(0.5))
|
.foregroundStyle(isHovered ? Color("mode").opacity(0.8) : Color("mode").opacity(0.5))
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
// Refresh Apps list
|
withAnimation {
|
||||||
reload.toggle()
|
// Refresh Apps list
|
||||||
let sortedApps = getSortedApps()
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
|
||||||
appState.sortedApps.userApps = sortedApps.userApps
|
|
||||||
appState.sortedApps.systemApps = sortedApps.systemApps
|
|
||||||
reload.toggle()
|
reload.toggle()
|
||||||
|
let sortedApps = getSortedApps()
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||||
|
appState.sortedApps.userApps = sortedApps.userApps
|
||||||
|
appState.sortedApps.systemApps = sortedApps.systemApps
|
||||||
|
reload.toggle()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if trash {
|
// if trash {
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ func checkFullDiskAccessForApp() -> Bool {
|
|||||||
// Check for access to Full Disk access
|
// Check for access to Full Disk access
|
||||||
func checkAndRequestFullDiskAccess(appState: AppState, skipAlert: Bool = false) -> Bool {
|
func checkAndRequestFullDiskAccess(appState: AppState, skipAlert: Bool = false) -> Bool {
|
||||||
@AppStorage("settings.permissions.disk") var diskP: Bool = false
|
@AppStorage("settings.permissions.disk") var diskP: Bool = false
|
||||||
|
@AppStorage("settings.permissions.hasLaunched") var hasLaunched: Bool = false
|
||||||
|
|
||||||
let process = Process()
|
let process = Process()
|
||||||
process.launchPath = "/usr/bin/sqlite3"
|
process.launchPath = "/usr/bin/sqlite3"
|
||||||
@@ -137,6 +138,14 @@ func checkAndRequestAccessibilityAccess(appState: AppState) -> Bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check if app is installed in /Applications directory
|
||||||
|
func isAppInApplicationsDir() -> Bool {
|
||||||
|
if let bundlePath = Bundle.main.bundlePath as NSString? {
|
||||||
|
return bundlePath.deletingLastPathComponent == "/Applications"
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Check if appearance is dark mode
|
// Check if appearance is dark mode
|
||||||
func isDarkModeEnabled() -> Bool {
|
func isDarkModeEnabled() -> Bool {
|
||||||
let process = Process()
|
let process = Process()
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ struct PearcleanerApp: App {
|
|||||||
@AppStorage("settings.updater.updateTimeframe") private var updateTimeframe: Int = 1
|
@AppStorage("settings.updater.updateTimeframe") private var updateTimeframe: Int = 1
|
||||||
@AppStorage("settings.permissions.disk") private var diskP: Bool = false
|
@AppStorage("settings.permissions.disk") private var diskP: Bool = false
|
||||||
@AppStorage("settings.permissions.events") private var diskE: Bool = false
|
@AppStorage("settings.permissions.events") private var diskE: Bool = false
|
||||||
|
@AppStorage("settings.permissions.hasLaunched") private var hasLaunched: Bool = false
|
||||||
@AppStorage("displayMode") var displayMode: DisplayMode = .system
|
@AppStorage("displayMode") var displayMode: DisplayMode = .system
|
||||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
@State private var search = ""
|
@State private var search = ""
|
||||||
@@ -53,13 +54,16 @@ struct PearcleanerApp: App {
|
|||||||
// Make sure App Support folder exists in the future if needed for storage
|
// Make sure App Support folder exists in the future if needed for storage
|
||||||
ensureApplicationSupportFolderExists(appState: appState)
|
ensureApplicationSupportFolderExists(appState: appState)
|
||||||
|
|
||||||
// Check for updates
|
// Check for updates 1 minute after app launch
|
||||||
if diskP && diskE {
|
if diskP {
|
||||||
loadGithubReleases(appState: appState)
|
loadGithubReleases(appState: appState)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for disk/accessibility permissions
|
// Check for disk/accessibility permissions just once on initial app launch
|
||||||
_ = checkAndRequestFullDiskAccess(appState: appState)
|
if !hasLaunched {
|
||||||
|
_ = checkAndRequestFullDiskAccess(appState: appState)
|
||||||
|
hasLaunched = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// TIMERS ////////////////////////////////////////////////////////////////////////////////////
|
// TIMERS ////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ func checkForUpdate(appState: AppState, manual: Bool = false) {
|
|||||||
guard let latestRelease = appState.releases.first else { return }
|
guard let latestRelease = appState.releases.first else { return }
|
||||||
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
||||||
if latestRelease.tag_name > currentVersion ?? "" {
|
if latestRelease.tag_name > currentVersion ?? "" {
|
||||||
NewWin.show(appState: appState, width: 500, height: 400, newWin: .update)
|
NewWin.show(appState: appState, width: 500, height: 440, newWin: .update)
|
||||||
// appState.alertType = .update
|
// appState.alertType = .update
|
||||||
// appState.showAlert = true
|
// appState.showAlert = true
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -91,24 +91,24 @@ struct FilesView: View {
|
|||||||
}
|
}
|
||||||
if appState.appInfo.appName.count < 5 {
|
if appState.appInfo.appName.count < 5 {
|
||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
Button(""){
|
Image(systemName: "exclamationmark.triangle.fill")
|
||||||
showPop = true
|
.foregroundStyle(.red)
|
||||||
}
|
.popover(isPresented: $showPop, arrowEdge: .top) {
|
||||||
.buttonStyle(SimpleButtonStyle(icon: "exclamationmark.triangle.fill", help: "Warning", color: .red))
|
VStack() {
|
||||||
.popover(isPresented: $showPop, arrowEdge: .top) {
|
Text("Pearcleaner searches for files via a combination of bundle id and app name.\n**\(appState.appInfo.appName)** has a common or short app name so there might be unrelated files found.\nPlease check the list thoroughly before uninstalling.")
|
||||||
VStack() {
|
.padding()
|
||||||
Text("Pearcleaner searches for files with a combination of bundle id and app name.\n**\(appState.appInfo.appName)** has a common or short app name so there might be unrelated files found.\nPlease check the list thoroughly before uninstalling.")
|
.font(.title2)
|
||||||
.padding(20)
|
}
|
||||||
.font(.title2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
Text("Warning")
|
||||||
Text("Read caution message")
|
|
||||||
.foregroundStyle(Color.red)
|
.foregroundStyle(Color.red)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.padding(.top)
|
.padding(.top)
|
||||||
|
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
showPop = true
|
showPop = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,38 +54,38 @@ struct TopBarMini: View {
|
|||||||
if appState.currentView == .apps {
|
if appState.currentView == .apps {
|
||||||
Button("") {
|
Button("") {
|
||||||
withAnimation(.easeInOut(duration: 0.5)) {
|
withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
updateOnMain {
|
// updateOnMain {
|
||||||
appState.currentView = .empty
|
appState.currentView = .empty
|
||||||
appState.appInfo = AppInfo.empty
|
appState.appInfo = AppInfo.empty
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.buttonStyle(SimpleButtonStyle(icon: "arrow.down.app", help: "Drop", color: Color("mode")))
|
.buttonStyle(SimpleButtonStyle(icon: "arrow.down.app", help: "Drop", color: Color("mode")))
|
||||||
} else if appState.currentView == .empty {
|
} else if appState.currentView == .empty {
|
||||||
Button("") {
|
Button("") {
|
||||||
withAnimation(.easeInOut(duration: 0.5)) {
|
withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
updateOnMain {
|
// updateOnMain {
|
||||||
appState.currentView = .apps
|
appState.currentView = .apps
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.buttonStyle(SimpleButtonStyle(icon: "list.triangle", help: "Apps", color: Color("mode")))
|
.buttonStyle(SimpleButtonStyle(icon: "list.triangle", help: "Apps", color: Color("mode")))
|
||||||
} else if appState.currentView == .files {
|
} else if appState.currentView == .files {
|
||||||
Button("") {
|
Button("") {
|
||||||
withAnimation(.easeInOut(duration: 0.5)) {
|
withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
updateOnMain {
|
// updateOnMain {
|
||||||
appState.currentView = .empty
|
appState.currentView = .empty
|
||||||
appState.appInfo = AppInfo.empty
|
appState.appInfo = AppInfo.empty
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.buttonStyle(SimpleButtonStyle(icon: "arrow.down.app", help: "Drop", color: Color("mode")))
|
.buttonStyle(SimpleButtonStyle(icon: "arrow.down.app", help: "Drop", color: Color("mode")))
|
||||||
Button("") {
|
Button("") {
|
||||||
withAnimation(.easeInOut(duration: 0.5)) {
|
withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
updateOnMain {
|
// updateOnMain {
|
||||||
appState.currentView = .apps
|
appState.currentView = .apps
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.buttonStyle(SimpleButtonStyle(icon: "list.triangle", help: "Apps", color: Color("mode")))
|
.buttonStyle(SimpleButtonStyle(icon: "list.triangle", help: "Apps", color: Color("mode")))
|
||||||
@@ -107,7 +107,7 @@ struct SearchBarMini: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
TextField("Search", text: $search)
|
TextField("Search", text: $search)
|
||||||
.textFieldStyle(AnimatedSearchStyle(text: $search))
|
.textFieldStyle(AnimatedSearchStyle(text: $search, reload: $reload))
|
||||||
// .textFieldStyle(SimpleSearchStyle(trash: true, reload: $reload, text: $search))
|
// .textFieldStyle(SimpleSearchStyle(trash: true, reload: $reload, text: $search))
|
||||||
}
|
}
|
||||||
// .frame(height: 20)
|
// .frame(height: 20)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import SwiftUI
|
|||||||
|
|
||||||
struct UpdateView: View {
|
struct UpdateView: View {
|
||||||
@EnvironmentObject var appState: AppState
|
@EnvironmentObject var appState: AppState
|
||||||
|
let isAppInAppsDir = isAppInApplicationsDir()
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
@@ -47,6 +48,18 @@ struct UpdateView: View {
|
|||||||
|
|
||||||
Spacer()
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
VStack() {
|
VStack() {
|
||||||
ProgressView("\(appState.progressBar.0)", value: appState.progressBar.1, total: 1.0)
|
ProgressView("\(appState.progressBar.0)", value: appState.progressBar.1, total: 1.0)
|
||||||
|
|||||||
Reference in New Issue
Block a user