Files
Pearcleaner/Pearcleaner/PearcleanerApp.swift
T
2024-05-28 17:16:22 -06:00

253 lines
10 KiB
Swift

//
// PearcleanerApp.swift
// Pearcleaner
//
// Created by Alin Lupascu on 10/31/23.
//
import SwiftUI
import AppKit
@main
struct PearcleanerApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@StateObject var appState = AppState()
@StateObject var locations = Locations()
@StateObject var fsm = FolderSettingsManager()
@State private var windowSettings = WindowSettings()
@AppStorage("settings.updater.updateTimeframe") private var updateTimeframe: Int = 1
@AppStorage("settings.permissions.hasLaunched") private var hasLaunched: Bool = false
@AppStorage("displayMode") var displayMode: DisplayMode = .system
@AppStorage("settings.general.mini") private var mini: Bool = false
@AppStorage("settings.general.miniview") private var miniView: Bool = true
@AppStorage("settings.general.features") private var features: String = ""
@AppStorage("settings.general.brew") private var brew: Bool = false
@AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false
@AppStorage("settings.menubar.mainWin") private var mainWinEnabled: Bool = false
@AppStorage("settings.interface.selectedMenubarIcon") var selectedMenubarIcon: String = "trash"
@State private var search = ""
@State private var showPopover: Bool = false
var body: some Scene {
WindowGroup {
Group {
if !mini {
RegularMode(search: $search, showPopover: $showPopover)
} else {
MiniMode(search: $search, showPopover: $showPopover)
}
}
.environmentObject(appState)
.environmentObject(locations)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
.handlesExternalEvents(preferring: Set(arrayLiteral: "pear"), allowing: Set(arrayLiteral: "*"))
.onOpenURL(perform: { url in
let deeplinkManager = DeeplinkManager(showPopover: $showPopover)
deeplinkManager.manage(url: url, appState: appState, locations: locations)
})
.onDrop(of: ["public.file-url"], isTargeted: nil) { providers, _ in
for provider in providers {
provider.loadItem(forTypeIdentifier: "public.file-url") { data, error in
if let data = data as? Data, let url = URL(dataRepresentation: data, relativeTo: nil) {
let deeplinkManager = DeeplinkManager(showPopover: $showPopover)
deeplinkManager.manage(url: url, appState: appState, locations: locations)
}
}
}
return true
}
// Save window size on window dimension change
.onChange(of: NSApplication.shared.windows.first?.frame) { newFrame in
if let newFrame = newFrame {
windowSettings.saveWindowSettings(frame: newFrame)
}
}
.alert(isPresented: $appState.showUninstallAlert) {
Alert(
title: Text("Warning!"),
message: Text("Pearcleaner and all of its files will be cleanly removed, are you sure?"),
primaryButton: .destructive(Text("Uninstall")) {
uninstallPearcleaner(appState: appState, locations: locations)
},
secondaryButton: .cancel()
)
}
.onAppear {
if miniView {
appState.currentView = .apps
} else {
appState.currentView = .empty
}
// Disable tabbing
NSWindow.allowsAutomaticWindowTabbing = false
// Get Apps
let sortedApps = getSortedApps(paths: fsm.folderPaths, appState: appState)
appState.sortedApps = sortedApps
// Enable menubar item
if menubarEnabled {
MenuBarExtraManager.shared.addMenuBarExtra(withView: {
MiniAppView(search: $search, showPopover: $showPopover, isMenuBar: true)
.environmentObject(locations)
.environmentObject(appState)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.preferredColorScheme(displayMode.colorScheme)
}, icon: selectedMenubarIcon)
}
#if !DEBUG
Task {
// Make sure App Support folder exists in the future if needed for storage
//MARK: This is not needed any longer as the update file is stored in /tmp directory
// ensureApplicationSupportFolderExists(appState: appState)
// Check for updates after app launch
checkAllPermissions(appState: appState) { results in
appState.permissionResults = results
if results.allPermissionsGranted {
loadGithubReleases(appState: appState)
getFeatures(appState: appState, features: $features)
}
}
// Load extra conditions from GitHub
loadConditionsFromGitHub()
// TIMERS ////////////////////////////////////////////////////////////////////////////////////
// Check for app updates every 8 hours or whatever user saved setting.
let updateSeconds = updateTimeframe.daysToSeconds
_ = Timer.scheduledTimer(withTimeInterval: updateSeconds, repeats: true) { _ in
DispatchQueue.main.async {
loadGithubReleases(appState: appState)
}
}
}
#endif
}
}
.windowStyle(.hiddenTitleBar)
.windowResizability(.contentMinSize)
.commands {
AppCommands(appState: appState, locations: locations, fsm: fsm)
// CommandGroup(replacing: .newItem, addition: { })
}
Settings {
SettingsView(showPopover: $showPopover, search: $search)
.environmentObject(appState)
.environmentObject(locations)
.environmentObject(fsm)
.environmentObject(ThemeSettings.shared)
.toolbarBackground(.clear)
.preferredColorScheme(displayMode.colorScheme)
}
}
}
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
var observer: NSObjectProtocol?
var windowSettings = WindowSettings()
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
let menubarEnabled = UserDefaults.standard.bool(forKey: "settings.menubar.enabled")
return !menubarEnabled
}
func applicationDidFinishLaunching(_ notification: Notification) {
let menubarEnabled = UserDefaults.standard.bool(forKey: "settings.menubar.enabled")
// UserDefaults.standard.register(defaults: ["NSQuitAlwaysKeepsWindows" : false])
findAndSetWindowFrame(named: ["Pearcleaner"], windowSettings: windowSettings)
if UserDefaults.standard.object(forKey: "themeColor") == nil {
self.appearanceChanged()
}
if menubarEnabled {
findAndHideWindows(named: ["Pearcleaner"])
NSApplication.shared.setActivationPolicy(.accessory)
}
// Start observing the appearance change
observer = DistributedNotificationCenter.default().addObserver(forName: NSNotification.Name(rawValue: "AppleInterfaceThemeChangedNotification"), object: nil, queue: OperationQueue.main) { [weak self] _ in
let themeMode = UserDefaults.standard.string(forKey: "settings.general.selectedTheme")
if themeMode == "Auto" {
self?.appearanceChanged()
}
}
}
func appearanceChanged() {
let dm = UserDefaults.standard.integer(forKey: "displayMode")
var displayMode = DisplayMode(rawValue: dm)
let dark = isDarkMode()
if dark {
displayMode?.colorScheme = .dark
} else {
displayMode?.colorScheme = .light
}
UserDefaults.standard.set(displayMode?.rawValue, forKey: "displayMode")
// Customize dark mode color to use Slate by default
ThemeSettings.shared.themeColor = isDarkMode() ? Color(.sRGB, red: 0.188143, green: 0.208556, blue: 0.262679, opacity: 1) : Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1)
// ThemeSettings.shared.themeColor = isDarkMode() ? Color(.sRGB, red: 0.149, green: 0.149, blue: 0.149, opacity: 1) : Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1)
ThemeSettings.shared.saveThemeColor()
}
func applicationWillTerminate(_ notification: Notification) {
// Stop observing the appearance change
if let observer = observer {
DistributedNotificationCenter.default().removeObserver(observer)
}
}
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
let windowSettings = WindowSettings()
if !flag {
// No visible windows, so let's open a new one
for window in sender.windows {
window.title = "Pearcleaner"
window.makeKeyAndOrderFront(self)
print(windowSettings.loadWindowSettings())
updateOnMain(after: 0.1, {
resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner")
print(window.title)
})
}
return true // Indicates you've handled the re-open
}
// Return true if you want the application to proceed with its default behavior
return false
}
}