Skip to content

Commit

Permalink
Updates
Browse files Browse the repository at this point in the history
  • Loading branch information
alienator88 committed Dec 17, 2024
1 parent 1e8c6dc commit 676fbed
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 80 deletions.
4 changes: 3 additions & 1 deletion Pearcleaner/Logic/AppState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class AppState: ObservableObject {
@Published var showUninstallAlert: Bool = false
@Published var externalMode: Bool = false
@Published var externalPaths: [URL] = [] // for handling multiple app from drops or deeplinks
@Published var selectedEnvironment: Path? // for handling dev environments


func getBundleSize(for appInfo: AppInfo, updateState: @escaping (Int64) -> Void) {
// Step 1: Check if the size is available and not 0 in the sortedApps cache
Expand Down Expand Up @@ -238,7 +240,7 @@ enum SortOption:Int, CaseIterable, Identifiable {
}


enum CurrentTabView:Int
enum CurrentTabView:Int, CaseIterable
{
case general
case interface
Expand Down
200 changes: 173 additions & 27 deletions Pearcleaner/Logic/DeepLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,49 @@ class DeeplinkManager {
@Binding var showPopover: Bool
private var urlQueue: [URL] = []
private var isProcessing = false
let updater: Updater
let fsm: FolderSettingsManager
@AppStorage("settings.general.mini") private var mini: Bool = false
@AppStorage("settings.general.oneshot") private var oneShotMode: Bool = false
@AppStorage("settings.general.selectedTab") private var selectedTab: CurrentTabView = .general
@State private var windowController = WindowManager()

init(showPopover: Binding<Bool>) {
init(showPopover: Binding<Bool>, updater: Updater, fsm: FolderSettingsManager) {
_showPopover = showPopover
self.updater = updater
self.fsm = fsm
}

class DeepLinkConstants {
static let scheme = "pear"
static let host = "com.alienator88.Pearcleaner"
static let query = "path"
static let brew = "brew"
struct DeepLinkActions {
static let openPearcleaner = "openPearcleaner"
static let openSettings = "openSettings"
static let openPermissions = "openPermissions"
static let uninstallApp = "uninstallApp"
static let checkOrphanedFiles = "checkOrphanedFiles"
static let checkDevEnv = "checkDevEnv"
static let checkUpdates = "checkUpdates"
// static let addFolder = "addFolder"
// static let removeFolder = "removeFolder"
// static let addExcludeFolder = "addExcludeFolder"
// static let removeExcludeFolder = "removeExcludeFolder"
static let refreshAppsList = "refreshAppsList"
static let resetSettings = "resetSettings"

static let allActions = [
openPearcleaner,
openSettings,
openPermissions,
uninstallApp,
checkOrphanedFiles,
checkDevEnv,
checkUpdates,
// addFolder,
// removeFolder,
// addExcludeFolder,
// removeExcludeFolder,
refreshAppsList,
resetSettings
]
}

func manage(url: URL, appState: AppState, locations: Locations) {
Expand All @@ -33,11 +64,32 @@ class DeeplinkManager {
appState.externalMode = true
}

// Add URL to queue
guard let scheme = url.scheme, scheme == "pear" else {
handleAsPathOrDropped(url: url, appState: appState, locations: locations)
return
}

if let host = url.host, DeepLinkActions.allActions.contains(host) {
switch host {
case DeepLinkActions.uninstallApp:
handleAsPathOrDropped(url: url, appState: appState, locations: locations)
default:
if let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
let queryItems = components.queryItems {
handleAppFunctions(action: host, queryItems: queryItems, appState: appState, locations: locations)
} else {
handleAppFunctions(action: host, queryItems: [], appState: appState, locations: locations)
}
}
} else {
// Host is nil or not in actions, treat as dropped/path scenario
handleAsPathOrDropped(url: url, appState: appState, locations: locations)
}
}

private func handleAsPathOrDropped(url: URL, appState: AppState, locations: Locations) {
urlQueue.append(url)
processQueue(appState: appState, locations: locations)

// If no app is currently displayed, load the first app immediately
if appState.appInfo.isEmpty {
loadNextAppInfo(appState: appState, locations: locations)
}
Expand All @@ -51,7 +103,7 @@ class DeeplinkManager {
// Process the next URL in the queue
if nextURL.pathExtension == "app" {
handleDroppedApps(url: nextURL, appState: appState, locations: locations)
} else if nextURL.scheme == DeepLinkConstants.scheme, nextURL.host == DeepLinkConstants.host {
} else if nextURL.scheme == "pear" {
handleDeepLinkedApps(url: nextURL, appState: appState, locations: locations)
}

Expand Down Expand Up @@ -79,32 +131,63 @@ class DeeplinkManager {

func handleDeepLinkedApps(url: URL, appState: AppState, locations: Locations) {
if let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
let queryItems = components.queryItems,
let path = queryItems.first(where: { $0.name == DeepLinkConstants.query })?.value {
let queryItems = components.queryItems {

let pathURL = URL(fileURLWithPath: path)
// Check for "path" query item first
if let path = queryItems.first(where: { $0.name == "path" })?.value {
let pathURL = URL(fileURLWithPath: path)

// Add path only if it's not already in externalPaths
if !appState.externalPaths.contains(pathURL) {
appState.externalPaths.append(pathURL)
}
// Add path only if it's not already in externalPaths
if !appState.externalPaths.contains(pathURL) {
appState.externalPaths.append(pathURL)
}

// Load the first app in externalPaths if no app is currently loaded
if appState.appInfo.isEmpty {
loadNextAppInfo(appState: appState, locations: locations)
// Load the first app in externalPaths if no app is currently loaded
if appState.appInfo.isEmpty {
loadNextAppInfo(appState: appState, locations: locations)
}
}
// If "path" is not available, check for "name" query item
else if let name = queryItems.first(where: { $0.name == "name" })?.value?.lowercased() {
reloadAppsList(appState: appState, fsm: fsm) {
let matchType = queryItems.first(where: { $0.name == "matchType" })?.value?.lowercased() ?? "exact"

if let matchedApp = appState.sortedApps.first(where: { appInfo in
let appNameLowercased = appInfo.appName.lowercased()
switch matchType {
case "contains":
return appNameLowercased.contains(name)
case "exact":
return appNameLowercased == name
default:
return false
}
}) {
let pathURL = matchedApp.path

// Handle sentinel mode
// if let brewValue = queryItems.first(where: { $0.name == DeepLinkConstants.brew })?.value, brewValue == "true" {
// updateOnMain {
// appState.externalMode = true
// }
// }
// Add path only if it's not already in externalPaths
if !appState.externalPaths.contains(pathURL) {
appState.externalPaths.append(pathURL)
}

// Load the first app in externalPaths if no app is currently loaded
if appState.appInfo.isEmpty {
self.loadNextAppInfo(appState: appState, locations: locations)
}
} else {
printOS("No app found matching the name '\(name)' with matchType: \(matchType)")
}
}

} else {
printOS("No valid query items for 'path' or 'name' found in the URL.")
}
} else {
printOS("URL does not match the expected scheme and host")
printOS("URL does not match the expected scheme pear://")
}
}


private func loadNextAppInfo(appState: AppState, locations: Locations) {
guard let nextPath = appState.externalPaths.first else { return }

Expand All @@ -115,4 +198,67 @@ class DeeplinkManager {
showAppInFiles(appInfo: appInfo!, appState: appState, locations: locations, showPopover: $showPopover)
}

private func handleAppFunctions(action: String, queryItems: [URLQueryItem], appState: AppState, locations: Locations) {

switch action {
case DeepLinkActions.openPearcleaner:
break
case DeepLinkActions.openSettings:
openAppSettings()
if let page = queryItems.first(where: { $0.name == "name" })?.value {
let search = page.lowercased()
let allPages = CurrentTabView.allCases
if let matchedPage = allPages.first(where: { $0.title.lowercased().contains(search) }) {
updateOnMain() {
self.selectedTab = matchedPage
}
}
}
break
case DeepLinkActions.openPermissions:
windowController.open(with: PermissionsListView().ignoresSafeArea(), width: 300, height: 250, material: .hudWindow)
break
case DeepLinkActions.checkOrphanedFiles:
appState.currentPage = .orphans
break
case DeepLinkActions.checkDevEnv:
if let envName = queryItems.first(where: { $0.name == "name" })?.value {
let search = envName.lowercased()
let allEnvs = PathLibrary.getPaths()
if let matchedEnv = allEnvs.first(where: { $0.name.lowercased().contains(search) }) {
updateOnMain() {
appState.selectedEnvironment = matchedEnv
}
}
}
appState.currentPage = .development
break
case DeepLinkActions.checkUpdates:
updater.checkForUpdates(sheet: true)
break
// case DeepLinkActions.addFolder:
// // Placeholder
// break
// case DeepLinkActions.removeFolder:
// // Placeholder
// break
// case DeepLinkActions.addExcludeFolder:
// // Placeholder
// break
// case DeepLinkActions.removeExcludeFolder:
// // Placeholder
// break
case DeepLinkActions.refreshAppsList:
reloadAppsList(appState: appState, fsm: fsm)
break
case DeepLinkActions.resetSettings:
DispatchQueue.global(qos: .background).async {
UserDefaults.standard.dictionaryRepresentation().keys.forEach(UserDefaults.standard.removeObject(forKey:))
}
break
default:
break
}
}

}
3 changes: 2 additions & 1 deletion Pearcleaner/Logic/Logic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -386,14 +386,15 @@ func undoTrash(completion: @escaping () -> Void = {}) {


// Reload apps list
func reloadAppsList(appState: AppState, fsm: FolderSettingsManager, delay: Double = 0.0) {
func reloadAppsList(appState: AppState, fsm: FolderSettingsManager, delay: Double = 0.0, completion: @escaping () -> Void = {}) {
appState.reload = true
updateOnBackground(after: delay) {
let sortedApps = getSortedApps(paths: fsm.folderPaths)
// Update UI on the main thread
updateOnMain {
appState.sortedApps = sortedApps
appState.reload = false
completion()
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions Pearcleaner/Logic/Utilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,15 @@ func killApp(appId: String, completion: @escaping () -> Void = {}) {
}


// Open app settings
func openAppSettings() {
if #available(macOS 14.0, *) {
@Environment(\.openSettings) var openSettings
openSettings()
} else {
NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil)
}
}


// Check if file/folder name has localized variant
Expand Down
4 changes: 2 additions & 2 deletions Pearcleaner/PearcleanerApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ struct PearcleanerApp: App {
}

dispatchGroup.notify(queue: .main) {
let deeplinkManager = DeeplinkManager(showPopover: $showPopover)
let deeplinkManager = DeeplinkManager(showPopover: $showPopover, updater: updater, fsm: fsm)
for url in droppedURLs {
deeplinkManager.manage(url: url, appState: appState, locations: locations)
}
Expand All @@ -97,7 +97,7 @@ struct PearcleanerApp: App {
}

.onOpenURL(perform: { url in
let deeplinkManager = DeeplinkManager(showPopover: $showPopover)
let deeplinkManager = DeeplinkManager(showPopover: $showPopover, updater: updater, fsm: fsm)
deeplinkManager.manage(url: url, appState: appState, locations: locations)
})
.alert(isPresented: $appState.showUninstallAlert) {
Expand Down
1 change: 1 addition & 0 deletions Pearcleaner/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -9150,6 +9150,7 @@
}
},
"Select One" : {
"extractionState" : "stale",
"localizations" : {
"de" : {
"stringUnit" : {
Expand Down
18 changes: 5 additions & 13 deletions Pearcleaner/Views/AppSearchView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,20 +168,12 @@ struct AppSearchView: View {
.buttonStyle(SimpleButtonStyle(icon: "circle.fill", label: String(localized: "Orphaned Files"), help: String(localized: "Orphaned Files"), size: 5))


if #available(macOS 14.0, *) {
SettingsLink {}
.buttonStyle(SimpleButtonStyle(icon: "circle.fill", label: String(localized: "Settings"), help: String(localized: "Settings"), size: 5))
} else {
Button("Settings") {
if #available(macOS 13.0, *) {
NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil)
} else {
NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil)
}
showMenu = false
}
.buttonStyle(SimpleButtonStyle(icon: "circle.fill", label: String(localized: "Settings"), help: String(localized: "Settings"), size: 5))
Button("Settings") {
openAppSettings()
showMenu = false
}
.buttonStyle(SimpleButtonStyle(icon: "circle.fill", label: String(localized: "Settings"), help: String(localized: "Settings"), size: 5))


if menubarEnabled {
Button("Quit") {
Expand Down
Loading

0 comments on commit 676fbed

Please sign in to comment.