mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 06:09:01 +00:00
v1.2
This commit is contained in:
@@ -7,6 +7,8 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
C7045A282B03E71D00376976 /* MiniMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A272B03E71D00376976 /* MiniMode.swift */; };
|
||||||
|
C7045A2A2B03FAF000376976 /* TopBarMini.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A292B03FAF000376976 /* TopBarMini.swift */; };
|
||||||
C72893052AFD42E600C8C1CD /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72893042AFD42E600C8C1CD /* DeepLink.swift */; };
|
C72893052AFD42E600C8C1CD /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72893042AFD42E600C8C1CD /* DeepLink.swift */; };
|
||||||
C72893122AFD51EA00C8C1CD /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72893112AFD51EA00C8C1CD /* main.swift */; };
|
C72893122AFD51EA00C8C1CD /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72893112AFD51EA00C8C1CD /* main.swift */; };
|
||||||
C7575D382B0181FC006A600A /* FileWatcher in Frameworks */ = {isa = PBXBuildFile; productRef = C7575D372B0181FC006A600A /* FileWatcher */; };
|
C7575D382B0181FC006A600A /* FileWatcher in Frameworks */ = {isa = PBXBuildFile; productRef = C7575D372B0181FC006A600A /* FileWatcher */; };
|
||||||
@@ -59,6 +61,8 @@
|
|||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
C7045A272B03E71D00376976 /* MiniMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiniMode.swift; sourceTree = "<group>"; };
|
||||||
|
C7045A292B03FAF000376976 /* TopBarMini.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBarMini.swift; sourceTree = "<group>"; };
|
||||||
C72893042AFD42E600C8C1CD /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = "<group>"; };
|
C72893042AFD42E600C8C1CD /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = "<group>"; };
|
||||||
C728930F2AFD51EA00C8C1CD /* PearcleanerSentinel */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = PearcleanerSentinel; sourceTree = BUILT_PRODUCTS_DIR; };
|
C728930F2AFD51EA00C8C1CD /* PearcleanerSentinel */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = PearcleanerSentinel; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
C72893112AFD51EA00C8C1CD /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
C72893112AFD51EA00C8C1CD /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||||
@@ -206,9 +210,11 @@
|
|||||||
C77B90222AF2D616009CC655 /* FilesView.swift */,
|
C77B90222AF2D616009CC655 /* FilesView.swift */,
|
||||||
C76D08542AF89CDE00D07867 /* AppListView.swift */,
|
C76D08542AF89CDE00D07867 /* AppListView.swift */,
|
||||||
C7D31D472AFEB23700C7ED9E /* TopBar.swift */,
|
C7D31D472AFEB23700C7ED9E /* TopBar.swift */,
|
||||||
|
C7045A292B03FAF000376976 /* TopBarMini.swift */,
|
||||||
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */,
|
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */,
|
||||||
C7D31D522B0048BE00C7ED9E /* TrashView.swift */,
|
C7D31D522B0048BE00C7ED9E /* TrashView.swift */,
|
||||||
C7D31D582B00A49300C7ED9E /* DropTarget.swift */,
|
C7D31D582B00A49300C7ED9E /* DropTarget.swift */,
|
||||||
|
C7045A272B03E71D00376976 /* MiniMode.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -336,7 +342,9 @@
|
|||||||
C7D31D462AFDA90C00C7ED9E /* Sentinel.swift in Sources */,
|
C7D31D462AFDA90C00C7ED9E /* Sentinel.swift in Sources */,
|
||||||
C7D31D592B00A49300C7ED9E /* DropTarget.swift in Sources */,
|
C7D31D592B00A49300C7ED9E /* DropTarget.swift in Sources */,
|
||||||
C77B90062AF18E2E009CC655 /* ContentView.swift in Sources */,
|
C77B90062AF18E2E009CC655 /* ContentView.swift in Sources */,
|
||||||
|
C7045A2A2B03FAF000376976 /* TopBarMini.swift in Sources */,
|
||||||
C77B901C2AF193A3009CC655 /* Styles.swift in Sources */,
|
C77B901C2AF193A3009CC655 /* Styles.swift in Sources */,
|
||||||
|
C7045A282B03E71D00376976 /* MiniMode.swift in Sources */,
|
||||||
C72893052AFD42E600C8C1CD /* DeepLink.swift in Sources */,
|
C72893052AFD42E600C8C1CD /* DeepLink.swift in Sources */,
|
||||||
C76D084C2AF83C9D00D07867 /* Permissions.swift in Sources */,
|
C76D084C2AF83C9D00D07867 /* Permissions.swift in Sources */,
|
||||||
C76D084A2AF83C6E00D07867 /* General.swift in Sources */,
|
C76D084A2AF83C6E00D07867 /* General.swift in Sources */,
|
||||||
@@ -517,7 +525,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 = 2;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = BK8443AXLU;
|
DEVELOPMENT_TEAM = BK8443AXLU;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -534,7 +542,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.1;
|
MARKETING_VERSION = 1.2;
|
||||||
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;
|
||||||
@@ -551,7 +559,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 = 2;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = BK8443AXLU;
|
DEVELOPMENT_TEAM = BK8443AXLU;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -568,7 +576,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.1;
|
MARKETING_VERSION = 1.2;
|
||||||
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;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class AppState: ObservableObject
|
|||||||
@Published var selectedItems = Set<URL>()
|
@Published var selectedItems = Set<URL>()
|
||||||
@Published var alertType = AlertType.off
|
@Published var alertType = AlertType.off
|
||||||
@Published var currentView = CurrentDetailsView.empty
|
@Published var currentView = CurrentDetailsView.empty
|
||||||
|
// @Published var currentMode = CurrentMode.regular
|
||||||
@Published var showAlert: Bool = false
|
@Published var showAlert: Bool = false
|
||||||
@Published var sidebar: Bool = true
|
@Published var sidebar: Bool = true
|
||||||
@Published var isReminderVisible: Bool = false
|
@Published var isReminderVisible: Bool = false
|
||||||
@@ -74,8 +75,15 @@ enum CurrentDetailsView:Int
|
|||||||
{
|
{
|
||||||
case empty
|
case empty
|
||||||
case files
|
case files
|
||||||
|
case apps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//enum CurrentMode:Int
|
||||||
|
//{
|
||||||
|
// case regular
|
||||||
|
// case mini
|
||||||
|
//}
|
||||||
|
|
||||||
enum AlertType:Int
|
enum AlertType:Int
|
||||||
{
|
{
|
||||||
case diskAccess
|
case diskAccess
|
||||||
|
|||||||
@@ -17,15 +17,12 @@ class DeeplinkManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func manage(url: URL, appState: AppState) {
|
func manage(url: URL, appState: AppState) {
|
||||||
guard url.scheme == DeepLinkConstants.scheme,
|
if url.pathExtension == "app" {
|
||||||
|
handleAppBundle(url: url, appState: appState)
|
||||||
|
} else if url.scheme == DeepLinkConstants.scheme,
|
||||||
url.host == DeepLinkConstants.host,
|
url.host == DeepLinkConstants.host,
|
||||||
let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
|
let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
|
||||||
let queryItems = components.queryItems
|
let queryItems = components.queryItems {
|
||||||
else {
|
|
||||||
print("URL does not match the expected scheme and host")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if let path = queryItems.first(where: { $0.name == DeepLinkConstants.query })?.value {
|
if let path = queryItems.first(where: { $0.name == DeepLinkConstants.query })?.value {
|
||||||
let pathURL = URL(fileURLWithPath: path)
|
let pathURL = URL(fileURLWithPath: path)
|
||||||
let appInfo = getAppInfo(atPath: pathURL)
|
let appInfo = getAppInfo(atPath: pathURL)
|
||||||
@@ -37,5 +34,41 @@ class DeeplinkManager {
|
|||||||
} else {
|
} else {
|
||||||
print("No path query parameter found in the URL")
|
print("No path query parameter found in the URL")
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
print("URL does not match the expected scheme and host")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func manage(url: URL, appState: AppState) {
|
||||||
|
// guard url.scheme == DeepLinkConstants.scheme,
|
||||||
|
// url.host == DeepLinkConstants.host,
|
||||||
|
// let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
|
||||||
|
// let queryItems = components.queryItems
|
||||||
|
// else {
|
||||||
|
// print("URL does not match the expected scheme and host")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if let path = queryItems.first(where: { $0.name == DeepLinkConstants.query })?.value {
|
||||||
|
// let pathURL = URL(fileURLWithPath: path)
|
||||||
|
// let appInfo = getAppInfo(atPath: pathURL)
|
||||||
|
// updateOnMain {
|
||||||
|
// appState.appInfo = appInfo!
|
||||||
|
// findPathsForApp(appState: appState, appInfo: appState.appInfo)
|
||||||
|
// appState.currentView = .files
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// print("No path query parameter found in the URL")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
func handleAppBundle(url: URL, appState: AppState) {
|
||||||
|
let appInfo = getAppInfo(atPath: url)
|
||||||
|
updateOnMain {
|
||||||
|
appState.appInfo = appInfo!
|
||||||
|
findPathsForApp(appState: appState, appInfo: appState.appInfo)
|
||||||
|
appState.currentView = .files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,18 +28,18 @@ struct SimpleButtonStyle: ButtonStyle {
|
|||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.frame(width: 20)
|
.frame(width: 20)
|
||||||
.foregroundColor(color)
|
.foregroundColor(hovered ? color : color.opacity(0.5))
|
||||||
}
|
}
|
||||||
.padding(8)
|
.padding(8)
|
||||||
.background {
|
// .background {
|
||||||
if hovered && !(shield ?? false) {
|
// if hovered && !(shield ?? false) {
|
||||||
// Circle()
|
//// Circle()
|
||||||
|
//// .strokeBorder(Color("AccentColor"), lineWidth: 1)
|
||||||
|
// RoundedRectangle(cornerRadius: 8)
|
||||||
// .strokeBorder(Color("AccentColor"), lineWidth: 1)
|
// .strokeBorder(Color("AccentColor"), lineWidth: 1)
|
||||||
RoundedRectangle(cornerRadius: 8)
|
// }
|
||||||
.strokeBorder(Color("AccentColor"), lineWidth: 1)
|
//
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
|
||||||
.onHover { hovering in
|
.onHover { hovering in
|
||||||
withAnimation() {
|
withAnimation() {
|
||||||
hovered = hovering
|
hovered = hovering
|
||||||
|
|||||||
@@ -24,6 +24,15 @@ func updateOnBackground(_ updates: @escaping () -> Void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Resize window
|
||||||
|
func resizeWindow() {
|
||||||
|
if let window = NSApplication.shared.windows.first {
|
||||||
|
let newSize = NSSize(width: 500, height: 450)
|
||||||
|
window.setContentSize(newSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 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
|
||||||
@@ -268,8 +277,9 @@ func presentAlert(appState: AppState) -> Alert {
|
|||||||
|
|
||||||
switch appState.alertType {
|
switch appState.alertType {
|
||||||
case .update:
|
case .update:
|
||||||
return Alert(title: Text("Update Available 🥳"), message: Text("You may choose to install the update now, otherwise you may start the update later from Settings"), primaryButton: .default(Text("Install")) {
|
return Alert(title: Text("Update Available 🥳"), message: Text("You may choose to install the update now, otherwise you may check again later from Settings"), primaryButton: .default(Text("Open")) {
|
||||||
downloadUpdate(appState: appState)
|
// downloadUpdate(appState: appState)
|
||||||
|
launchUpdate()
|
||||||
appState.alertType = .off
|
appState.alertType = .off
|
||||||
}, secondaryButton: .cancel())
|
}, secondaryButton: .cancel())
|
||||||
case .no_update:
|
case .no_update:
|
||||||
@@ -278,7 +288,7 @@ func presentAlert(appState: AppState) -> Alert {
|
|||||||
appState.alertType = .off
|
appState.alertType = .off
|
||||||
})
|
})
|
||||||
case .diskAccess:
|
case .diskAccess:
|
||||||
return Alert(title: Text("Permissions"), message: Text("This application requires Full Disk and Accessibility permissions. Once you grant full disk, don't restart the app yet. Wait a second for a second prompt for accessibility and then restart"), primaryButton: .default(Text("Allow in Settings")) {
|
return Alert(title: Text("Permissions"), message: Text("This application requires Full Disk and Accessibility permissions. Drag the app into the Full Disk pane if not visible."), primaryButton: .default(Text("Allow in Settings")) {
|
||||||
if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") {
|
if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") {
|
||||||
NSWorkspace.shared.open(url)
|
NSWorkspace.shared.open(url)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,13 @@ struct PearcleanerApp: App {
|
|||||||
@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("displayMode") var displayMode: DisplayMode = .dark
|
@AppStorage("displayMode") var displayMode: DisplayMode = .dark
|
||||||
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
@State private var search = ""
|
@State private var search = ""
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
|
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
|
if !mini {
|
||||||
AppListView(search: $search)
|
AppListView(search: $search)
|
||||||
.environmentObject(appState)
|
.environmentObject(appState)
|
||||||
.frame(minWidth: 1020, minHeight: 700)
|
.frame(minWidth: 1020, minHeight: 700)
|
||||||
@@ -41,6 +43,56 @@ 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)
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
let destinationURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("Pearcleaner").appendingPathComponent("Pearcleaner.zip")
|
||||||
|
try? fileManager.removeItem(at: destinationURL)
|
||||||
|
|
||||||
|
// Check for updates
|
||||||
|
if diskP && diskE {
|
||||||
|
loadGithubReleases(appState: appState)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for disk/accessibility permissions
|
||||||
|
_ = checkAndRequestFullDiskAccess(appState: appState)
|
||||||
|
|
||||||
|
|
||||||
|
// TIMERS ////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Check for app updates every 8 hours or whatever user saved setting. Also refresh autosuggestion list
|
||||||
|
let updateSeconds = updateTimeframe.daysToSeconds
|
||||||
|
_ = Timer.scheduledTimer(withTimeInterval: updateSeconds, repeats: true) { _ in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
loadGithubReleases(appState: appState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MiniMode(search: $search)
|
||||||
|
.environmentObject(appState)
|
||||||
|
.frame(minWidth: 500, minHeight: 450)
|
||||||
|
.preferredColorScheme(displayMode.colorScheme)
|
||||||
|
.alert(isPresented: $appState.showAlert) { presentAlert(appState: appState) }
|
||||||
|
.handlesExternalEvents(preferring: Set(arrayLiteral: "pear"), allowing: Set(arrayLiteral: "*"))
|
||||||
|
.onAppear {
|
||||||
|
NSWindow.allowsAutomaticWindowTabbing = false
|
||||||
|
|
||||||
|
// Get Apps
|
||||||
|
let sortedApps = getSortedApps()
|
||||||
|
appState.sortedApps.userApps = sortedApps.userApps
|
||||||
|
appState.sortedApps.systemApps = sortedApps.systemApps
|
||||||
|
|
||||||
|
Task {
|
||||||
|
|
||||||
|
#if !DEBUG
|
||||||
|
|
||||||
|
// Make sure App Support folder exists in the future if needed for storage
|
||||||
|
ensureApplicationSupportFolderExists(appState: appState)
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
let destinationURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("Pearcleaner").appendingPathComponent("Pearcleaner.zip")
|
||||||
|
try? fileManager.removeItem(at: destinationURL)
|
||||||
|
|
||||||
// Check for updates
|
// Check for updates
|
||||||
if diskP && diskE {
|
if diskP && diskE {
|
||||||
@@ -65,6 +117,8 @@ struct PearcleanerApp: App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
.windowStyle(.hiddenTitleBar)
|
.windowStyle(.hiddenTitleBar)
|
||||||
.windowToolbarStyle(.unified)
|
.windowToolbarStyle(.unified)
|
||||||
.windowResizability(.contentMinSize)
|
.windowResizability(.contentMinSize)
|
||||||
@@ -73,6 +127,7 @@ struct PearcleanerApp: App {
|
|||||||
CommandGroup(replacing: .newItem, addition: { })
|
CommandGroup(replacing: .newItem, addition: { })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Settings {
|
Settings {
|
||||||
SettingsView()
|
SettingsView()
|
||||||
.environmentObject(appState)
|
.environmentObject(appState)
|
||||||
@@ -89,4 +144,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// func application(_ sender: NSApplication, open urls: [URL]) {
|
||||||
|
// for url in urls {
|
||||||
|
// if url.pathExtension == "app" {
|
||||||
|
// print("Dropped app path:", url.path)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "logo_combo.png",
|
"filename" : "logo_combo_small.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "pear_small.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -2,6 +2,21 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>CFBundleDocumentTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>Application Bundle</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Default</string>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>LSItemContentTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>com.apple.application-bundle</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ struct AboutSettingsTab: View {
|
|||||||
.buttonStyle(SimpleButtonStyle(icon: "cloud", help: "Update application", color: Color("AccentColor")))
|
.buttonStyle(SimpleButtonStyle(icon: "cloud", help: "Update application", color: Color("AccentColor")))
|
||||||
|
|
||||||
Button("") {
|
Button("") {
|
||||||
NSWorkspace.shared.open(URL(string: "https://github.com/ghosted-labs/Pearcleaner")!)
|
NSWorkspace.shared.open(URL(string: "https://github.com/alienator88/Pearcleaner")!)
|
||||||
}
|
}
|
||||||
.buttonStyle(SimpleButtonStyle(icon: "paperplane", help: "View repository", color: Color("AccentColor")))
|
.buttonStyle(SimpleButtonStyle(icon: "paperplane", help: "View repository", color: Color("AccentColor")))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ import SwiftUI
|
|||||||
struct GeneralSettingsTab: View {
|
struct GeneralSettingsTab: View {
|
||||||
@EnvironmentObject var appState: AppState
|
@EnvironmentObject var appState: AppState
|
||||||
@AppStorage("settings.general.glass") private var glass: Bool = true
|
@AppStorage("settings.general.glass") private var glass: Bool = true
|
||||||
// @AppStorage("settings.general.ants") private var ants: Bool = false
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
|
@AppStorage("settings.general.dark") var isDark: Bool = true
|
||||||
|
@AppStorage("displayMode") var displayMode: DisplayMode = .dark
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
@@ -34,30 +36,54 @@ struct GeneralSettingsTab: View {
|
|||||||
.fill(Color("mode").opacity(0.05))
|
.fill(Color("mode").opacity(0.05))
|
||||||
)
|
)
|
||||||
|
|
||||||
// HStack {
|
HStack {
|
||||||
// VStack(alignment: .leading, spacing: 5) {
|
VStack(alignment: .leading, spacing: 5) {
|
||||||
// Text("Animation").font(.title2)
|
Text("Dark Mode").font(.title2)
|
||||||
// Text("Toggles the marching ants animation on the drop zone")
|
Text("Toggles between dark and light mode")
|
||||||
// .font(.footnote)
|
.font(.footnote)
|
||||||
// .foregroundStyle(.gray)
|
.foregroundStyle(.gray)
|
||||||
// }
|
}
|
||||||
// Spacer()
|
Spacer()
|
||||||
// Toggle(isOn: $ants, label: {
|
Toggle(isOn: $isDark, label: {
|
||||||
// })
|
})
|
||||||
// .toggleStyle(.switch)
|
.toggleStyle(.switch)
|
||||||
// }
|
.onChange(of: isDark) { newValue in
|
||||||
// .padding()
|
displayMode.colorScheme = newValue ? .dark : .light
|
||||||
// .background(
|
}
|
||||||
// RoundedRectangle(cornerRadius: 8)
|
}
|
||||||
// .fill(Color("mode").opacity(0.05))
|
.padding()
|
||||||
// )
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.fill(Color("mode").opacity(0.05))
|
||||||
|
)
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
VStack(alignment: .leading, spacing: 5) {
|
||||||
|
Text("Mini").font(.title2)
|
||||||
|
Text("Toggles a smaller, unified view with hidden app list")
|
||||||
|
.font(.footnote)
|
||||||
|
.foregroundStyle(.gray)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
Toggle(isOn: $mini, label: {
|
||||||
|
})
|
||||||
|
.toggleStyle(.switch)
|
||||||
|
.onChange(of: mini) { newVal in
|
||||||
|
resizeWindow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.fill(Color("mode").opacity(0.05))
|
||||||
|
)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
.padding(20)
|
.padding(20)
|
||||||
.frame(width: 400, height: 100)
|
.frame(width: 400, height: 250)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,6 +124,11 @@ func checkForUpdate(appState: AppState, manual: Bool = false) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func launchUpdate() {
|
||||||
|
NSWorkspace.shared.open(URL(string: "https://github.com/alienator88/Pearcleaner/releases")!)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func downloadUpdate(appState: AppState) {
|
func downloadUpdate(appState: AppState) {
|
||||||
guard let latestRelease = appState.releases.first else { return }
|
guard let latestRelease = appState.releases.first else { return }
|
||||||
guard let asset = latestRelease.assets.first else { return }
|
guard let asset = latestRelease.assets.first else { return }
|
||||||
@@ -140,7 +145,7 @@ func downloadUpdate(appState: AppState) {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
if fileManager.fileExists(atPath: destinationURL.path) {
|
if fileManager.fileExists(atPath: destinationURL.path) {
|
||||||
try fileManager.removeItem(at: destinationURL)
|
try? fileManager.removeItem(at: destinationURL)
|
||||||
}
|
}
|
||||||
try fileManager.moveItem(at: localURL, to: destinationURL)
|
try fileManager.moveItem(at: localURL, to: destinationURL)
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,24 @@ struct AppListView: View {
|
|||||||
VStack(alignment: .center) {
|
VStack(alignment: .center) {
|
||||||
|
|
||||||
VStack(alignment: .center, spacing: 20) {
|
VStack(alignment: .center, spacing: 20) {
|
||||||
|
HStack {
|
||||||
SearchBar(search: $search)
|
SearchBar(search: $search)
|
||||||
|
|
||||||
|
Button("") {
|
||||||
|
withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "arrow.triangle.2.circlepath", help: "Refresh app list", color: Color("mode")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
.padding(.top, 20)
|
.padding(.top, 20)
|
||||||
@@ -157,20 +174,20 @@ struct AppDetailsEmptyView: View {
|
|||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if appState.isReminderVisible {
|
// if appState.isReminderVisible {
|
||||||
Text(" + Z to undo")
|
// Text(" + Z to undo")
|
||||||
.font(.title2)
|
// .font(.title2)
|
||||||
.foregroundStyle(Color("AccentColor").opacity(0.5))
|
// .foregroundStyle(Color("AccentColor").opacity(0.5))
|
||||||
.fontWeight(.medium)
|
// .fontWeight(.medium)
|
||||||
.onAppear {
|
// .onAppear {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
// DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
||||||
withAnimation {
|
// withAnimation {
|
||||||
appState.isReminderVisible = false
|
// appState.isReminderVisible = false
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
Spacer()
|
// Spacer()
|
||||||
}
|
// }
|
||||||
|
|
||||||
Text("Drop an app above or select one from the list to begin")
|
Text("Drop an app above or select one from the list to begin")
|
||||||
.font(.title3)
|
.font(.title3)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import UniformTypeIdentifiers
|
|||||||
|
|
||||||
struct DropTarget: View, DropDelegate {
|
struct DropTarget: View, DropDelegate {
|
||||||
@AppStorage("settings.general.ants") private var ants: Bool = false
|
@AppStorage("settings.general.ants") private var ants: Bool = false
|
||||||
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
@ObservedObject var appState: AppState
|
@ObservedObject var appState: AppState
|
||||||
@State private var shouldFlash = false
|
@State private var shouldFlash = false
|
||||||
private var types: [UTType] = [.fileURL]
|
private var types: [UTType] = [.fileURL]
|
||||||
@@ -34,15 +35,16 @@ struct DropTarget: View, DropDelegate {
|
|||||||
|
|
||||||
ZStack {
|
ZStack {
|
||||||
LinearGradient(gradient: Gradient(colors: [.pink, .orange]), startPoint: .leading, endPoint: .trailing)
|
LinearGradient(gradient: Gradient(colors: [.pink, .orange]), startPoint: .leading, endPoint: .trailing)
|
||||||
.mask(Image("logo")
|
.mask(
|
||||||
|
Image(mini ? "pear" : "logo")
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFit())
|
.scaledToFit()
|
||||||
|
.frame(width: mini ? 104 : 500)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.frame(width: 500, height: 200)
|
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -71,6 +73,8 @@ struct DropTarget: View, DropDelegate {
|
|||||||
}
|
}
|
||||||
.onDrop(of: types, delegate: self)
|
.onDrop(of: types, delegate: self)
|
||||||
.padding(20)
|
.padding(20)
|
||||||
|
.frame(maxWidth: mini ? 200 : .infinity)
|
||||||
|
.frame(maxHeight: mini ? 200 : .infinity)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ struct FilesView: View {
|
|||||||
@State private var showDetails: Bool = false
|
@State private var showDetails: Bool = false
|
||||||
@State private var showPop: Bool = false
|
@State private var showPop: Bool = false
|
||||||
@State private var itemDetails: [(size: String, icon: Image?)] = []
|
@State private var itemDetails: [(size: String, icon: Image?)] = []
|
||||||
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
@Environment(\.colorScheme) var colorScheme
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@@ -34,8 +35,9 @@ struct FilesView: View {
|
|||||||
if let appIcon = appState.appInfo.appIcon {
|
if let appIcon = appState.appInfo.appIcon {
|
||||||
Image(nsImage: appIcon)
|
Image(nsImage: appIcon)
|
||||||
.resizable()
|
.resizable()
|
||||||
.aspectRatio(contentMode: .fit)
|
.scaledToFit()
|
||||||
.frame(width: 100, height: 100)
|
// .aspectRatio(contentMode: .fit)
|
||||||
|
.frame(width: 50, height: 50)
|
||||||
.padding(.leading)
|
.padding(.leading)
|
||||||
}
|
}
|
||||||
//app title, size and items
|
//app title, size and items
|
||||||
@@ -43,19 +45,19 @@ struct FilesView: View {
|
|||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
VStack(alignment: .leading, spacing: 5){
|
VStack(alignment: .leading, spacing: 5){
|
||||||
HStack {
|
HStack {
|
||||||
Text("\(appState.appInfo.appName)").font(.title).fontWeight(.bold)
|
Text("\(appState.appInfo.appName)").font(mini ? .title2 :.title).fontWeight(.bold)
|
||||||
// .foregroundStyle(Color("AccentColor"))
|
// .foregroundStyle(Color("AccentColor"))
|
||||||
Text("•").foregroundStyle(Color("AccentColor"))
|
Text("•").foregroundStyle(Color("AccentColor"))
|
||||||
Text("\(appState.appInfo.appVersion)").font(.title3)
|
Text("\(appState.appInfo.appVersion)").font(.title3)
|
||||||
// .foregroundStyle(.gray.opacity(0.8))
|
// .foregroundStyle(.gray.opacity(0.8))
|
||||||
}
|
}
|
||||||
Text("\(appState.appInfo.bundleIdentifier)").font(.title3)
|
Text("\(appState.appInfo.bundleIdentifier)").font(mini ? .footnote :.title3)
|
||||||
.foregroundStyle((.gray.opacity(0.8)))
|
.foregroundStyle((.gray.opacity(0.8)))
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text("\(appSize)").font(.title).fontWeight(.bold)
|
Text("\(appSize)").font(mini ? .title2 :.title).fontWeight(.bold)
|
||||||
// .foregroundStyle(Color("AccentColor"))
|
// .foregroundStyle(Color("AccentColor"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +182,7 @@ struct FilesView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
.frame(minWidth: 700)
|
// .frame(minWidth: 700)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
Task {
|
Task {
|
||||||
calculateFileDetails()
|
calculateFileDetails()
|
||||||
@@ -291,7 +293,7 @@ struct FileDetailsItem: View {
|
|||||||
Button("") {
|
Button("") {
|
||||||
NSWorkspace.shared.selectFile(path.path, inFileViewerRootedAtPath: path.deletingLastPathComponent().path)
|
NSWorkspace.shared.selectFile(path.path, inFileViewerRootedAtPath: path.deletingLastPathComponent().path)
|
||||||
}
|
}
|
||||||
.buttonStyle(SimpleButtonStyle(icon: "folder.fill", help: "Show in Finder", color: Color("AccentColor")))
|
.buttonStyle(SimpleButtonStyle(icon: "folder.fill", help: "Show in Finder", color: Color("mode")))
|
||||||
|
|
||||||
}
|
}
|
||||||
.background(
|
.background(
|
||||||
|
|||||||
@@ -0,0 +1,203 @@
|
|||||||
|
//
|
||||||
|
// MiniMode.swift
|
||||||
|
// Pearcleaner
|
||||||
|
//
|
||||||
|
// Created by Alin Lupascu on 11/14/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct MiniMode: View {
|
||||||
|
@EnvironmentObject var appState: AppState
|
||||||
|
@Binding var search: String
|
||||||
|
@State private var showSys: Bool = true
|
||||||
|
@State private var showUsr: Bool = true
|
||||||
|
@State private var reload: Bool = false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
|
||||||
|
HStack(alignment: .center, spacing: 0) {
|
||||||
|
|
||||||
|
// Main Mini View
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
if appState.currentView == .empty {
|
||||||
|
TopBarMini(reload: $reload, search: $search)
|
||||||
|
AppDetailsEmptyView()
|
||||||
|
} else if appState.currentView == .files {
|
||||||
|
TopBarMini(reload: $reload, search: $search)
|
||||||
|
FilesView()
|
||||||
|
.id(appState.appInfo.id)
|
||||||
|
} else if appState.currentView == .apps {
|
||||||
|
TopBarMini(reload: $reload, search: $search)
|
||||||
|
MiniAppView(search: $search, reload: $reload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// .padding(.leading, appState.sidebar ? 0 : 10)
|
||||||
|
.transition(.move(edge: .leading))
|
||||||
|
|
||||||
|
}
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
.onOpenURL(perform: { url in
|
||||||
|
let deeplinkManager = DeeplinkManager()
|
||||||
|
deeplinkManager.manage(url: url, appState: appState)
|
||||||
|
})
|
||||||
|
// MARK: Background for whole app
|
||||||
|
// .background(Color("bg").opacity(1))
|
||||||
|
// .background(VisualEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct MiniEmptyView: View {
|
||||||
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
@EnvironmentObject var appState: AppState
|
||||||
|
@State private var animateGradient: Bool = false
|
||||||
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack() {
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
DropTarget(appState: appState)
|
||||||
|
.frame(maxWidth: mini ? 300 : 500)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
if appState.isReminderVisible {
|
||||||
|
Text("CMD + Z to undo")
|
||||||
|
.font(.title2)
|
||||||
|
.foregroundStyle(Color("AccentColor").opacity(0.5))
|
||||||
|
.fontWeight(.medium)
|
||||||
|
.onAppear {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
||||||
|
withAnimation {
|
||||||
|
appState.isReminderVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
|
||||||
|
Text("Drop an app above or select one from the list to begin")
|
||||||
|
.font(.title3)
|
||||||
|
.padding(.bottom, 25)
|
||||||
|
.opacity(0.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct MiniAppView: View {
|
||||||
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
@EnvironmentObject var appState: AppState
|
||||||
|
@State private var animateGradient: Bool = false
|
||||||
|
@Binding var search: String
|
||||||
|
@State private var showSys: Bool = true
|
||||||
|
@State private var showUsr: Bool = true
|
||||||
|
@Binding var reload: Bool
|
||||||
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
|
||||||
|
var filteredUserApps: [AppInfo] {
|
||||||
|
if search.isEmpty {
|
||||||
|
return appState.sortedApps.userApps
|
||||||
|
} else {
|
||||||
|
return appState.sortedApps.userApps.filter { $0.appName.localizedCaseInsensitiveContains(search) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var filteredSystemApps: [AppInfo] {
|
||||||
|
if search.isEmpty {
|
||||||
|
return appState.sortedApps.systemApps
|
||||||
|
} else {
|
||||||
|
return appState.sortedApps.systemApps.filter { $0.appName.localizedCaseInsensitiveContains(search) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ZStack {
|
||||||
|
HStack(spacing: 0){
|
||||||
|
|
||||||
|
if reload {
|
||||||
|
VStack {
|
||||||
|
Spacer()
|
||||||
|
ProgressView("Refreshing applications")
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.vertical)
|
||||||
|
} else {
|
||||||
|
VStack(alignment: .center) {
|
||||||
|
|
||||||
|
// VStack(alignment: .center, spacing: 20) {
|
||||||
|
// HStack {
|
||||||
|
// SearchBar(search: $search)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// .padding(.horizontal, 5)
|
||||||
|
// .padding(.bottom)
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
|
||||||
|
if filteredUserApps.count > 0 {
|
||||||
|
VStack {
|
||||||
|
Header(title: "User", count: filteredUserApps.count)
|
||||||
|
ForEach(filteredUserApps, id: \.self) { appInfo in
|
||||||
|
AppListItems(appInfo: appInfo)
|
||||||
|
if appInfo != filteredUserApps.last {
|
||||||
|
Divider().padding(.horizontal, 5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.bottom)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if filteredSystemApps.count > 0 {
|
||||||
|
VStack {
|
||||||
|
Header(title: "System", count: filteredSystemApps.count)
|
||||||
|
ForEach(filteredSystemApps, id: \.self) { appInfo in
|
||||||
|
AppListItems(appInfo: appInfo)
|
||||||
|
if appInfo != filteredSystemApps.last {
|
||||||
|
Divider().padding(.horizontal, 5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
}
|
||||||
|
.scrollIndicators(.never)
|
||||||
|
|
||||||
|
}
|
||||||
|
.padding(.vertical)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.transition(.move(edge: .leading))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -14,52 +14,14 @@ struct TopBar: View {
|
|||||||
@AppStorage("displayMode") var displayMode: DisplayMode = .system
|
@AppStorage("displayMode") var displayMode: DisplayMode = .system
|
||||||
@AppStorage("settings.general.glass") private var glass: Bool = false
|
@AppStorage("settings.general.glass") private var glass: Bool = false
|
||||||
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
|
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
|
||||||
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
@EnvironmentObject var appState: AppState
|
@EnvironmentObject var appState: AppState
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(alignment: .center, spacing: 20) {
|
HStack(alignment: .center, spacing: 20) {
|
||||||
|
|
||||||
HStack(alignment: .center, spacing: 5) {
|
|
||||||
// Button("") {
|
|
||||||
// withAnimation(.easeInOut(duration: 0.5)) {
|
|
||||||
// appState.sidebar.toggle()
|
|
||||||
// if appState.sidebar {
|
|
||||||
// appState.winWidth += 300
|
|
||||||
// } else {
|
|
||||||
// appState.winWidth -= 300
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .buttonStyle(SimpleButtonStyle(icon: "sidebar.left", help: "Toggle Sidebar", color: Color("AccentColor")))
|
|
||||||
|
|
||||||
|
|
||||||
Button("") {
|
|
||||||
withAnimation(.easeInOut(duration: 0.5)) {
|
|
||||||
// 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()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.buttonStyle(SimpleButtonStyle(icon: "arrow.circlepath", help: "Refresh app list", color: Color("AccentColor")))
|
|
||||||
|
|
||||||
Button("") {
|
|
||||||
withAnimation(.easeInOut(duration: 0.5)) {
|
|
||||||
openTrash()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.buttonStyle(SimpleButtonStyle(icon: "trash", help: "Open Trash", color: Color("AccentColor")))
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if appState.currentView != .empty {
|
if appState.currentView != .empty {
|
||||||
Button("") {
|
Button("") {
|
||||||
withAnimation(.easeInOut(duration: 0.5)) {
|
withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
@@ -67,48 +29,38 @@ struct TopBar: View {
|
|||||||
appState.appInfo = AppInfo.empty
|
appState.appInfo = AppInfo.empty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.buttonStyle(SimpleButtonStyle(icon: "house", help: "Home", color: Color("AccentColor")))
|
.buttonStyle(SimpleButtonStyle(icon: "arrow.down.app", help: "Drop", color: Color("mode")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if appState.isReminderVisible {
|
||||||
|
Text("CMD + Z to undo")
|
||||||
|
.font(.title2)
|
||||||
|
.foregroundStyle(Color("AccentColor").opacity(0.5))
|
||||||
|
.fontWeight(.medium)
|
||||||
|
.onAppear {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
||||||
|
withAnimation {
|
||||||
|
appState.isReminderVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
HStack(alignment: .center, spacing: 5) {
|
|
||||||
|
|
||||||
if sentinel {
|
|
||||||
Button("") {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
.buttonStyle(SimpleButtonStyle(icon: "lock.shield", help: "Sentinel enabled", color: .green, shield: true))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Button("") {
|
// if sentinel {
|
||||||
withAnimation(.easeInOut(duration: 0.5)) {
|
// Button("") {
|
||||||
glass.toggle()
|
// //
|
||||||
}
|
// }
|
||||||
}
|
// .buttonStyle(SimpleButtonStyle(icon: "lock.shield", help: "Sentinel enabled", color: .green, shield: true))
|
||||||
.buttonStyle(SimpleButtonStyle(icon: glass ? "circle.dashed" : "circle.dashed.inset.filled", help: "Toggle transparency", color: Color("AccentColor")))
|
// }
|
||||||
|
|
||||||
Button("") {
|
|
||||||
withAnimation(.easeInOut(duration: 0.5)) {
|
|
||||||
if displayMode.colorScheme == .dark {
|
|
||||||
displayMode.colorScheme = .light
|
|
||||||
} else if displayMode.colorScheme == .light {
|
|
||||||
displayMode.colorScheme = .dark
|
|
||||||
} else {
|
|
||||||
displayMode.colorScheme = .dark
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.buttonStyle(SimpleButtonStyle(icon: displayMode.colorScheme == .dark ? "sun.max.fill" : "moon.fill", help: "Toggle appearance", color: Color("AccentColor")))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 20)
|
.padding(.horizontal, 20)
|
||||||
.padding(.top, 30)
|
.padding(.top, 20)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
//
|
||||||
|
// TopBarMini.swift
|
||||||
|
// Pearcleaner
|
||||||
|
//
|
||||||
|
// Created by Alin Lupascu on 11/14/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct TopBarMini: View {
|
||||||
|
@Binding var reload: Bool
|
||||||
|
@AppStorage("displayMode") var displayMode: DisplayMode = .system
|
||||||
|
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
|
||||||
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
|
@Binding var search: String
|
||||||
|
@EnvironmentObject var appState: AppState
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack(alignment: .center, spacing: 5) {
|
||||||
|
Spacer()
|
||||||
|
if appState.currentView == .apps {
|
||||||
|
HStack(alignment: .center, spacing: 0) {
|
||||||
|
Button("") {
|
||||||
|
withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
|
appState.currentView = .empty
|
||||||
|
appState.appInfo = AppInfo.empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "arrow.down.app", help: "Drop", color: Color("mode")))
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if appState.currentView == .empty {
|
||||||
|
Button("") {
|
||||||
|
withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
|
appState.currentView = .apps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "list.triangle", help: "Apps", color: Color("mode")))
|
||||||
|
} else if appState.currentView == .files {
|
||||||
|
Button("") {
|
||||||
|
withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
|
appState.currentView = .empty
|
||||||
|
appState.appInfo = AppInfo.empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "arrow.down.app", help: "Drop", color: Color("mode")))
|
||||||
|
Button("") {
|
||||||
|
withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
|
appState.currentView = .apps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "list.triangle", help: "Apps", color: Color("mode")))
|
||||||
|
}
|
||||||
|
|
||||||
|
if appState.currentView == .apps {
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
SearchBarMini(search: $search)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Button("") {
|
||||||
|
withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "arrow.triangle.2.circlepath", help: "Refresh app list", color: Color("mode")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
// if sentinel {
|
||||||
|
// Button("") {
|
||||||
|
// //
|
||||||
|
// }
|
||||||
|
// .buttonStyle(SimpleButtonStyle(icon: "lock.shield", help: "Sentinel enabled", color: .green, shield: true))
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
.padding(.horizontal)
|
||||||
|
.padding(.top, 30)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct SearchBarMini: View {
|
||||||
|
@Binding var search: String
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
TextField("Search", text: $search)
|
||||||
|
.textFieldStyle(SimpleSearchStyle(trash: true, text: $search))
|
||||||
|
}
|
||||||
|
.frame(height: 30)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user