🎉 ShotScreen v1.0 - Initial Release
🚀 First official release of ShotScreen with complete feature set: ✨ Core Features: - Advanced screenshot capture system - Multi-monitor support - Professional UI/UX design - Automated update system with Sparkle - Apple notarized & code signed 🛠 Technical Excellence: - Native Swift macOS application - Professional build & deployment pipeline - Comprehensive error handling - Memory optimized performance 📦 Distribution Ready: - Professional DMG packaging - Apple notarization complete - No security warnings for users - Ready for public distribution This is the foundation release that establishes ShotScreen as a premium screenshot tool for macOS.
This commit is contained in:
962
ShotScreen/Sources/SettingsManager.swift
Normal file
962
ShotScreen/Sources/SettingsManager.swift
Normal file
@@ -0,0 +1,962 @@
|
||||
import AppKit
|
||||
import SwiftUI // Needed for ObservableObject, Published, etc.
|
||||
import Combine // Needed for objectWillChange
|
||||
|
||||
class SettingsManager: ObservableObject {
|
||||
static let shared = SettingsManager()
|
||||
private var isInitializing = false // NIEUWE FLAG
|
||||
|
||||
var screenshotFolder: String? {
|
||||
get { UserDefaults.standard.string(forKey: SettingsKey.screenshotFolder) }
|
||||
set {
|
||||
objectWillChange.send()
|
||||
UserDefaults.standard.set(newValue, forKey: SettingsKey.screenshotFolder)
|
||||
}
|
||||
}
|
||||
|
||||
var thumbnailTimer: Int {
|
||||
get { UserDefaults.standard.integer(forKey: SettingsKey.thumbnailTimer) }
|
||||
set { objectWillChange.send(); UserDefaults.standard.set(newValue, forKey: SettingsKey.thumbnailTimer) }
|
||||
}
|
||||
|
||||
@Published var closeAfterDrag: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(closeAfterDrag, forKey: SettingsKey.closeAfterDrag)
|
||||
if !isInitializing {
|
||||
NotificationCenter.default.post(name: .closeAfterDragSettingChanged, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var showFolderButton: Bool {
|
||||
get { UserDefaults.standard.bool(forKey: SettingsKey.showFolderButton) }
|
||||
set { objectWillChange.send(); UserDefaults.standard.set(newValue, forKey: SettingsKey.showFolderButton) }
|
||||
}
|
||||
|
||||
@Published var closeAfterSave: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(closeAfterSave, forKey: SettingsKey.closeAfterSave)
|
||||
if !isInitializing {
|
||||
NotificationCenter.default.post(name: .closeAfterSaveSettingChanged, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Published var playSoundOnCapture: Bool = true {
|
||||
didSet {
|
||||
UserDefaults.standard.set(playSoundOnCapture, forKey: SettingsKey.playSoundOnCapture)
|
||||
if !isInitializing { // CONTROLEER FLAG
|
||||
NotificationCenter.default.post(name: .playSoundOnCaptureSettingChanged, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Properties for filename format
|
||||
var filenamePrefix: String {
|
||||
get { UserDefaults.standard.string(forKey: SettingsKey.filenamePrefix) ?? "Schermafbeelding" } // Default prefix
|
||||
set { objectWillChange.send(); UserDefaults.standard.set(newValue, forKey: SettingsKey.filenamePrefix) }
|
||||
}
|
||||
|
||||
var filenameFormatPreset: FilenameFormatPreset {
|
||||
get {
|
||||
let rawValue = UserDefaults.standard.integer(forKey: SettingsKey.filenameFormatPreset)
|
||||
return FilenameFormatPreset(rawValue: rawValue) ?? .macOSStyle // Default naar macOS style
|
||||
}
|
||||
set { objectWillChange.send(); UserDefaults.standard.set(newValue.rawValue, forKey: SettingsKey.filenameFormatPreset) }
|
||||
}
|
||||
|
||||
var filenameCustomFormat: String {
|
||||
get { UserDefaults.standard.string(forKey: SettingsKey.filenameCustomFormat) ?? "{YYYY}-{MM}-{DD}_{hh}.{mm}.{ss}" } // Default custom format
|
||||
set { objectWillChange.send(); UserDefaults.standard.set(newValue, forKey: SettingsKey.filenameCustomFormat) }
|
||||
}
|
||||
|
||||
// NEW: Properties for action enables
|
||||
var isRenameActionEnabled: Bool {
|
||||
get {
|
||||
let value = UserDefaults.standard.object(forKey: SettingsKey.isRenameActionEnabled) as? Bool ?? true
|
||||
return value
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
UserDefaults.standard.set(newValue, forKey: SettingsKey.isRenameActionEnabled)
|
||||
}
|
||||
}
|
||||
var isStashActionEnabled: Bool {
|
||||
get {
|
||||
let value = UserDefaults.standard.object(forKey: SettingsKey.isStashActionEnabled) as? Bool ?? true
|
||||
return value
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
UserDefaults.standard.set(newValue, forKey: SettingsKey.isStashActionEnabled)
|
||||
}
|
||||
}
|
||||
var isOCRActionEnabled: Bool {
|
||||
get {
|
||||
let value = UserDefaults.standard.object(forKey: SettingsKey.isOCRActionEnabled) as? Bool ?? true
|
||||
return value
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
UserDefaults.standard.set(newValue, forKey: SettingsKey.isOCRActionEnabled)
|
||||
}
|
||||
}
|
||||
var isClipboardActionEnabled: Bool {
|
||||
get {
|
||||
let value = UserDefaults.standard.object(forKey: SettingsKey.isClipboardActionEnabled) as? Bool ?? true
|
||||
return value
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
UserDefaults.standard.set(newValue, forKey: SettingsKey.isClipboardActionEnabled)
|
||||
}
|
||||
}
|
||||
var isBackgroundRemoveActionEnabled: Bool {
|
||||
get {
|
||||
let value = UserDefaults.standard.object(forKey: SettingsKey.isBackgroundRemoveActionEnabled) as? Bool ?? true
|
||||
return value
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
UserDefaults.standard.set(newValue, forKey: SettingsKey.isBackgroundRemoveActionEnabled)
|
||||
}
|
||||
}
|
||||
var isCancelActionEnabled: Bool {
|
||||
get {
|
||||
let value = UserDefaults.standard.object(forKey: SettingsKey.isCancelActionEnabled) as? Bool ?? true
|
||||
return value
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
UserDefaults.standard.set(newValue, forKey: SettingsKey.isCancelActionEnabled)
|
||||
}
|
||||
}
|
||||
var isRemoveActionEnabled: Bool {
|
||||
get {
|
||||
let value = UserDefaults.standard.object(forKey: SettingsKey.isRemoveActionEnabled) as? Bool ?? true
|
||||
return value
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
UserDefaults.standard.set(newValue, forKey: SettingsKey.isRemoveActionEnabled)
|
||||
}
|
||||
}
|
||||
var isAction3Enabled: Bool {
|
||||
get {
|
||||
let value = UserDefaults.standard.object(forKey: SettingsKey.isAction3Enabled) as? Bool ?? false
|
||||
return value
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
UserDefaults.standard.set(newValue, forKey: SettingsKey.isAction3Enabled)
|
||||
}
|
||||
}
|
||||
var isAction4Enabled: Bool {
|
||||
get {
|
||||
let value = UserDefaults.standard.object(forKey: SettingsKey.isAction4Enabled) as? Bool ?? false
|
||||
return value
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
UserDefaults.standard.set(newValue, forKey: SettingsKey.isAction4Enabled)
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Properties for Delete and Cancel Drag actions
|
||||
var isDeleteActionEnabled: Bool {
|
||||
get { UserDefaults.standard.object(forKey: SettingsKey.isDeleteActionEnabled) as? Bool ?? true } // Default AAN
|
||||
set { objectWillChange.send(); UserDefaults.standard.set(newValue, forKey: SettingsKey.isDeleteActionEnabled) }
|
||||
}
|
||||
var isCancelDragActionEnabled: Bool {
|
||||
get { UserDefaults.standard.object(forKey: SettingsKey.isCancelDragActionEnabled) as? Bool ?? true } // Default AAN
|
||||
set { objectWillChange.send(); UserDefaults.standard.set(newValue, forKey: SettingsKey.isCancelDragActionEnabled) }
|
||||
}
|
||||
|
||||
// 🎨 Background Removal Method Preference
|
||||
var preferredBackgroundRemovalMethod: BackgroundRemovalMethod {
|
||||
get {
|
||||
let rawValue = UserDefaults.standard.string(forKey: SettingsKey.preferredBackgroundRemovalMethod) ?? "auto"
|
||||
return BackgroundRemovalMethod(rawValue: rawValue) ?? .auto
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
UserDefaults.standard.set(newValue.rawValue, forKey: SettingsKey.preferredBackgroundRemovalMethod)
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Property for automatic startup
|
||||
@Published var startAppOnLogin: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(startAppOnLogin, forKey: SettingsKey.startAppOnLogin)
|
||||
if !isInitializing {
|
||||
NotificationCenter.default.post(name: .startAppOnLoginSettingChanged, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Property for automatic screenshot saving
|
||||
var autoSaveScreenshot: Bool {
|
||||
get { UserDefaults.standard.object(forKey: SettingsKey.autoSaveScreenshot) as? Bool ?? false } // Default false (handmatig beslissen)
|
||||
set {
|
||||
objectWillChange.send()
|
||||
UserDefaults.standard.set(newValue, forKey: SettingsKey.autoSaveScreenshot)
|
||||
// Optioneel: post een notificatie als directe actie in ScreenshotApp nodig is
|
||||
// NotificationCenter.default.post(name: .autoSaveScreenshotChanged, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// VOEG DEZE TOE
|
||||
static let thumbnailFixedSizeKey = "thumbnailFixedSize"
|
||||
@Published var thumbnailFixedSize: ThumbnailFixedSize = .medium {
|
||||
didSet {
|
||||
UserDefaults.standard.set(thumbnailFixedSize.rawValue, forKey: SettingsManager.thumbnailFixedSizeKey)
|
||||
if !isInitializing {
|
||||
NotificationCenter.default.post(name: .thumbnailSizeSettingChanged, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Property for stash always on top
|
||||
@Published var stashAlwaysOnTop: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(stashAlwaysOnTop, forKey: SettingsKey.stashAlwaysOnTop)
|
||||
if !isInitializing {
|
||||
NotificationCenter.default.post(name: .stashAlwaysOnTopSettingChanged, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 🔥💎 MEGA NIEUWE STASH PREVIEW SIZE SETTING! 💎🔥
|
||||
@Published var stashPreviewSize: StashPreviewSize = .medium {
|
||||
didSet {
|
||||
UserDefaults.standard.set(stashPreviewSize.rawValue, forKey: SettingsKey.stashPreviewSize)
|
||||
if !isInitializing {
|
||||
NotificationCenter.default.post(name: .stashPreviewSizeChanged, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 🔥💥⚡ HYPERMODE STASH GRID CONFIGURATION! ⚡💥🔥
|
||||
@Published var stashGridMode: StashGridMode = .fixedColumns {
|
||||
didSet {
|
||||
UserDefaults.standard.set(stashGridMode.rawValue, forKey: SettingsKey.stashGridMode)
|
||||
if !isInitializing {
|
||||
NotificationCenter.default.post(name: .stashGridModeChanged, object: nil)
|
||||
NotificationCenter.default.post(name: .stashGridConfigChanged, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Published var stashMaxColumns: Int = 2 {
|
||||
didSet {
|
||||
// Clamp to 1-5 range for HYPERMODE safety!
|
||||
let clampedValue = max(1, min(5, stashMaxColumns))
|
||||
if clampedValue != stashMaxColumns {
|
||||
stashMaxColumns = clampedValue
|
||||
return
|
||||
}
|
||||
UserDefaults.standard.set(stashMaxColumns, forKey: SettingsKey.stashMaxColumns)
|
||||
if !isInitializing {
|
||||
NotificationCenter.default.post(name: .stashGridConfigChanged, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Published var stashMaxRows: Int = 1 {
|
||||
didSet {
|
||||
// Clamp to 1-5 range for HYPERMODE safety!
|
||||
let clampedValue = max(1, min(5, stashMaxRows))
|
||||
if clampedValue != stashMaxRows {
|
||||
stashMaxRows = clampedValue
|
||||
return
|
||||
}
|
||||
UserDefaults.standard.set(stashMaxRows, forKey: SettingsKey.stashMaxRows)
|
||||
if !isInitializing {
|
||||
NotificationCenter.default.post(name: .stashGridConfigChanged, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 🔥 NIEUW: Persistent stash setting
|
||||
@Published var persistentStash: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(persistentStash, forKey: SettingsKey.persistentStash)
|
||||
if !isInitializing {
|
||||
NotificationCenter.default.post(name: .persistentStashChanged, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 🔄 UPDATE SETTINGS
|
||||
@Published var automaticUpdates: Bool = true {
|
||||
didSet {
|
||||
UserDefaults.standard.set(automaticUpdates, forKey: SettingsKey.automaticUpdates)
|
||||
}
|
||||
}
|
||||
|
||||
@Published var includePreReleases: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(includePreReleases, forKey: SettingsKey.includePreReleases)
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Property for hiding desktop icons during screenshots
|
||||
@Published var hideDesktopIconsDuringScreenshot: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(hideDesktopIconsDuringScreenshot, forKey: SettingsKey.hideDesktopIconsDuringScreenshot)
|
||||
if !isInitializing {
|
||||
NotificationCenter.default.post(name: .hideDesktopIconsSettingChanged, object: nil)
|
||||
}
|
||||
print("Setting updated: hideDesktopIconsDuringScreenshot = \(hideDesktopIconsDuringScreenshot)")
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Property for hiding desktop widgets during screenshots
|
||||
@Published var hideDesktopWidgetsDuringScreenshot: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(hideDesktopWidgetsDuringScreenshot, forKey: SettingsKey.hideDesktopWidgetsDuringScreenshot)
|
||||
if !isInitializing {
|
||||
NotificationCenter.default.post(name: .hideDesktopWidgetsSettingChanged, object: nil)
|
||||
}
|
||||
print("Setting updated: hideDesktopWidgetsDuringScreenshot = \(hideDesktopWidgetsDuringScreenshot)")
|
||||
}
|
||||
}
|
||||
|
||||
// 🔊 NEW: Sound volume and type settings
|
||||
@Published var screenshotSoundVolume: Float = 0.1 {
|
||||
didSet {
|
||||
UserDefaults.standard.set(screenshotSoundVolume, forKey: SettingsKey.screenshotSoundVolume)
|
||||
}
|
||||
}
|
||||
|
||||
@Published var screenshotSoundType: ScreenshotSoundType = .pop {
|
||||
didSet {
|
||||
if let data = try? JSONEncoder().encode(screenshotSoundType) {
|
||||
UserDefaults.standard.set(data, forKey: SettingsKey.screenshotSoundType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 🗂️ NEW: Cache management settings
|
||||
@Published var cacheRetentionTime: CacheRetentionTime = .oneWeek {
|
||||
didSet {
|
||||
if let data = try? JSONEncoder().encode(cacheRetentionTime) {
|
||||
UserDefaults.standard.set(data, forKey: SettingsKey.cacheRetentionTime)
|
||||
}
|
||||
// 🧪 NIEUW: Send notification for cache retention time changes (except during initialization)
|
||||
if !isInitializing {
|
||||
NotificationCenter.default.post(name: NSNotification.Name("cacheRetentionTimeChanged"), object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NEW: Property for first launch completion
|
||||
var hasCompletedFirstLaunch: Bool {
|
||||
get { UserDefaults.standard.bool(forKey: SettingsKey.hasCompletedFirstLaunch) }
|
||||
set {
|
||||
objectWillChange.send()
|
||||
UserDefaults.standard.set(newValue, forKey: SettingsKey.hasCompletedFirstLaunch)
|
||||
}
|
||||
}
|
||||
|
||||
var windowCaptureIncludeCursor: Bool {
|
||||
get { UserDefaults.standard.bool(forKey: SettingsKey.windowCaptureIncludeCursor) }
|
||||
set {
|
||||
objectWillChange.send()
|
||||
UserDefaults.standard.set(newValue, forKey: SettingsKey.windowCaptureIncludeCursor)
|
||||
if !isInitializing {
|
||||
NotificationCenter.default.post(name: .windowCaptureSettingChanged, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Property for clean desktop screenshots
|
||||
// VERWIJDERD: cleanDesktopScreenshots property - feature disabled
|
||||
|
||||
@Published var saveAfterEdit: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(saveAfterEdit, forKey: SettingsKey.saveAfterEdit)
|
||||
NotificationCenter.default.post(name: .saveAfterEditSettingChanged, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
var thumbnailDisplayScreen: ThumbnailDisplayScreen {
|
||||
get {
|
||||
let rawValue = UserDefaults.standard.string(forKey: SettingsKey.thumbnailDisplayScreen) ?? ThumbnailDisplayScreen.automatic.rawValue
|
||||
return ThumbnailDisplayScreen(rawValue: rawValue) ?? .automatic
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
UserDefaults.standard.set(newValue.rawValue, forKey: SettingsKey.thumbnailDisplayScreen)
|
||||
}
|
||||
}
|
||||
|
||||
static let actionOrderKey = "actionOrder"
|
||||
@Published var actionOrder: [ActionType] = [] {
|
||||
didSet {
|
||||
let orderStrings = actionOrder.map { $0.rawValue }
|
||||
UserDefaults.standard.set(orderStrings, forKey: SettingsManager.actionOrderKey)
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Keyboard shortcut properties
|
||||
@Published var useCustomShortcut: Bool = false {
|
||||
didSet {
|
||||
UserDefaults.standard.set(useCustomShortcut, forKey: SettingsKey.useCustomShortcut)
|
||||
if !isInitializing {
|
||||
// Post notification for hotkey change
|
||||
NotificationCenter.default.post(name: .shortcutSettingChanged, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Published var customShortcutModifiers: UInt = 0 {
|
||||
didSet {
|
||||
UserDefaults.standard.set(customShortcutModifiers, forKey: SettingsKey.customShortcutModifiers)
|
||||
if !isInitializing && useCustomShortcut {
|
||||
NotificationCenter.default.post(name: .shortcutSettingChanged, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Published var customShortcutKey: UInt16 = 0 {
|
||||
didSet {
|
||||
UserDefaults.standard.set(customShortcutKey, forKey: SettingsKey.customShortcutKey)
|
||||
if !isInitializing && useCustomShortcut {
|
||||
NotificationCenter.default.post(name: .shortcutSettingChanged, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private init() {
|
||||
isInitializing = true // ZET FLAG
|
||||
|
||||
// Laad ALLE properties hier uit UserDefaults
|
||||
self.screenshotFolder = UserDefaults.standard.string(forKey: SettingsKey.screenshotFolder) // Kan nil zijn
|
||||
self.filenamePrefix = UserDefaults.standard.string(forKey: SettingsKey.filenamePrefix) ?? "Schermafbeelding"
|
||||
|
||||
let presetRaw = UserDefaults.standard.integer(forKey: SettingsKey.filenameFormatPreset)
|
||||
self.filenameFormatPreset = FilenameFormatPreset(rawValue: presetRaw) ?? .macOSStyle
|
||||
// Als de opgeslagen rawValue 0 was en .macOSStyle is 0, dan is dit onnodig dubbel.
|
||||
// Overweeg: if UserDefaults.standard.object(forKey: SettingsKey.filenameFormatPreset) != nil { ... laden ... } else { default }
|
||||
|
||||
self.filenameCustomFormat = UserDefaults.standard.string(forKey: SettingsKey.filenameCustomFormat) ?? "{YYYY}-{MM}-{DD}_{hh}.{mm}.{ss}"
|
||||
self.saveAfterEdit = UserDefaults.standard.bool(forKey: SettingsKey.saveAfterEdit) // Default false als key niet bestaat
|
||||
self.playSoundOnCapture = UserDefaults.standard.object(forKey: SettingsKey.playSoundOnCapture) as? Bool ?? true
|
||||
self.thumbnailTimer = UserDefaults.standard.integer(forKey: SettingsKey.thumbnailTimer)
|
||||
self.closeAfterDrag = UserDefaults.standard.bool(forKey: SettingsKey.closeAfterDrag)
|
||||
|
||||
let thumbFixedSizeKeyString = SettingsManager.thumbnailFixedSizeKey
|
||||
let loadedFixedSizeRawValue = UserDefaults.standard.string(forKey: thumbFixedSizeKeyString)
|
||||
if let loadedSize = loadedFixedSizeRawValue.flatMap(ThumbnailFixedSize.init) {
|
||||
if self.thumbnailFixedSize != loadedSize {
|
||||
self.thumbnailFixedSize = loadedSize
|
||||
}
|
||||
} else {
|
||||
UserDefaults.standard.set(ThumbnailFixedSize.medium.rawValue, forKey: thumbFixedSizeKeyString)
|
||||
}
|
||||
|
||||
self.showFolderButton = UserDefaults.standard.object(forKey: SettingsKey.showFolderButton) as? Bool ?? true
|
||||
self.startAppOnLogin = UserDefaults.standard.object(forKey: SettingsKey.startAppOnLogin) as? Bool ?? false
|
||||
self.autoSaveScreenshot = UserDefaults.standard.object(forKey: SettingsKey.autoSaveScreenshot) as? Bool ?? false
|
||||
self.closeAfterSave = UserDefaults.standard.bool(forKey: SettingsKey.closeAfterSave)
|
||||
|
||||
// Stash window border etc. ook hier initialiseren:
|
||||
// self.stashWindowBorderWidth = UserDefaults.standard.object(forKey: SettingsKey.stashWindowBorderWidth) == nil ? 1.0 : CGFloat(UserDefaults.standard.float(forKey: SettingsKey.stashWindowBorderWidth))
|
||||
self.isRenameActionEnabled = UserDefaults.standard.object(forKey: SettingsKey.isRenameActionEnabled) as? Bool ?? true
|
||||
self.isStashActionEnabled = UserDefaults.standard.object(forKey: SettingsKey.isStashActionEnabled) as? Bool ?? true
|
||||
self.isOCRActionEnabled = UserDefaults.standard.object(forKey: SettingsKey.isOCRActionEnabled) as? Bool ?? true
|
||||
self.isClipboardActionEnabled = UserDefaults.standard.object(forKey: SettingsKey.isClipboardActionEnabled) as? Bool ?? true
|
||||
self.isBackgroundRemoveActionEnabled = UserDefaults.standard.object(forKey: SettingsKey.isBackgroundRemoveActionEnabled) as? Bool ?? true
|
||||
self.isCancelActionEnabled = UserDefaults.standard.object(forKey: SettingsKey.isCancelActionEnabled) as? Bool ?? true
|
||||
self.isRemoveActionEnabled = UserDefaults.standard.object(forKey: SettingsKey.isRemoveActionEnabled) as? Bool ?? true
|
||||
self.isDeleteActionEnabled = UserDefaults.standard.object(forKey: SettingsKey.isDeleteActionEnabled) as? Bool ?? true
|
||||
self.isCancelDragActionEnabled = UserDefaults.standard.object(forKey: SettingsKey.isCancelDragActionEnabled) as? Bool ?? true
|
||||
self.stashAlwaysOnTop = UserDefaults.standard.object(forKey: SettingsKey.stashAlwaysOnTop) as? Bool ?? false
|
||||
self.hideDesktopIconsDuringScreenshot = UserDefaults.standard.object(forKey: SettingsKey.hideDesktopIconsDuringScreenshot) as? Bool ?? false
|
||||
self.hideDesktopWidgetsDuringScreenshot = UserDefaults.standard.object(forKey: SettingsKey.hideDesktopWidgetsDuringScreenshot) as? Bool ?? false
|
||||
|
||||
|
||||
// 🔥💎 MEGA NIEUWE STASH PREVIEW SIZE INIT! 💎🔥
|
||||
let stashPreviewSizeRaw = UserDefaults.standard.string(forKey: SettingsKey.stashPreviewSize) ?? StashPreviewSize.medium.rawValue
|
||||
self.stashPreviewSize = StashPreviewSize(rawValue: stashPreviewSizeRaw) ?? .medium
|
||||
|
||||
// 🔥💥⚡ HYPERMODE STASH GRID INIT! ⚡💥🔥
|
||||
let stashGridModeRaw = UserDefaults.standard.string(forKey: SettingsKey.stashGridMode) ?? StashGridMode.fixedColumns.rawValue
|
||||
self.stashGridMode = StashGridMode(rawValue: stashGridModeRaw) ?? .fixedColumns
|
||||
|
||||
self.stashMaxColumns = UserDefaults.standard.object(forKey: SettingsKey.stashMaxColumns) as? Int ?? 2
|
||||
// HYPERMODE SAFETY: Clamp to 1-5 range!
|
||||
self.stashMaxColumns = max(1, min(5, self.stashMaxColumns))
|
||||
|
||||
self.stashMaxRows = UserDefaults.standard.object(forKey: SettingsKey.stashMaxRows) as? Int ?? 1
|
||||
// HYPERMODE SAFETY: Clamp to 1-5 range!
|
||||
self.stashMaxRows = max(1, min(5, self.stashMaxRows))
|
||||
|
||||
// 🔥 NIEUW: Persistent stash init
|
||||
self.persistentStash = UserDefaults.standard.object(forKey: SettingsKey.persistentStash) as? Bool ?? false
|
||||
|
||||
// 🔄 UPDATE SETTINGS INIT
|
||||
self.automaticUpdates = UserDefaults.standard.object(forKey: SettingsKey.automaticUpdates) as? Bool ?? true
|
||||
self.includePreReleases = UserDefaults.standard.object(forKey: SettingsKey.includePreReleases) as? Bool ?? false
|
||||
|
||||
// 🎹 CUSTOM SHORTCUT SETTINGS INIT
|
||||
self.useCustomShortcut = UserDefaults.standard.object(forKey: SettingsKey.useCustomShortcut) as? Bool ?? false
|
||||
self.customShortcutModifiers = UserDefaults.standard.object(forKey: SettingsKey.customShortcutModifiers) as? UInt ?? 0
|
||||
self.customShortcutKey = UserDefaults.standard.object(forKey: SettingsKey.customShortcutKey) as? UInt16 ?? 0
|
||||
|
||||
// 🔊 SOUND SETTINGS INIT
|
||||
self.screenshotSoundVolume = UserDefaults.standard.object(forKey: SettingsKey.screenshotSoundVolume) as? Float ?? 0.1
|
||||
|
||||
if let soundTypeData = UserDefaults.standard.data(forKey: SettingsKey.screenshotSoundType),
|
||||
let soundType = try? JSONDecoder().decode(ScreenshotSoundType.self, from: soundTypeData) {
|
||||
self.screenshotSoundType = soundType
|
||||
} else {
|
||||
self.screenshotSoundType = .pop
|
||||
}
|
||||
|
||||
// 🗂️ CACHE MANAGEMENT INIT
|
||||
if let cacheRetentionData = UserDefaults.standard.data(forKey: SettingsKey.cacheRetentionTime),
|
||||
let retentionTime = try? JSONDecoder().decode(CacheRetentionTime.self, from: cacheRetentionData) {
|
||||
self.cacheRetentionTime = retentionTime
|
||||
} else {
|
||||
self.cacheRetentionTime = .oneWeek
|
||||
}
|
||||
|
||||
// Initialize thumbnailDisplayScreen
|
||||
let _ = UserDefaults.standard.string(forKey: SettingsKey.thumbnailDisplayScreen) ?? ThumbnailDisplayScreen.automatic.rawValue
|
||||
// No need to set it since it's a computed property
|
||||
|
||||
// Load action order with migration for new actions
|
||||
if let savedOrder = UserDefaults.standard.stringArray(forKey: SettingsManager.actionOrderKey) {
|
||||
var loadedOrder = savedOrder.compactMap { ActionType(rawValue: $0) }
|
||||
|
||||
// Migration: ensure all new actions are included
|
||||
let allActions = ActionType.allCases
|
||||
for action in allActions {
|
||||
if !loadedOrder.contains(action) {
|
||||
loadedOrder.append(action)
|
||||
}
|
||||
}
|
||||
|
||||
actionOrder = loadedOrder
|
||||
} else {
|
||||
actionOrder = ActionType.allCases
|
||||
}
|
||||
|
||||
// Initialize hasCompletedFirstLaunch
|
||||
self.hasCompletedFirstLaunch = UserDefaults.standard.object(forKey: SettingsKey.hasCompletedFirstLaunch) as? Bool ?? false
|
||||
|
||||
isInitializing = false // RESET FLAG
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case screenshotFolder
|
||||
case filenamePrefix
|
||||
case filenameFormatPreset
|
||||
case filenameCustomFormat
|
||||
case saveAfterEdit
|
||||
case playSoundOnCapture
|
||||
case thumbnailTimer
|
||||
case closeAfterDrag
|
||||
case thumbnailFixedSize
|
||||
case showFolderButton
|
||||
case startAppOnLogin
|
||||
case autoSaveScreenshot
|
||||
case closeAfterSave
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
screenshotFolder = try container.decodeIfPresent(String.self, forKey: .screenshotFolder)
|
||||
filenamePrefix = try container.decodeIfPresent(String.self, forKey: .filenamePrefix) ?? "Screenshot"
|
||||
filenameCustomFormat = try container.decodeIfPresent(String.self, forKey: .filenameCustomFormat) ?? "{YYYY}-{MM}-{DD}_{hh}.{mm}.{ss}"
|
||||
saveAfterEdit = try container.decodeIfPresent(Bool.self, forKey: .saveAfterEdit) ?? false
|
||||
playSoundOnCapture = try container.decodeIfPresent(Bool.self, forKey: .playSoundOnCapture) ?? true
|
||||
thumbnailTimer = try container.decodeIfPresent(Int.self, forKey: .thumbnailTimer) ?? 0
|
||||
closeAfterDrag = try container.decodeIfPresent(Bool.self, forKey: .closeAfterDrag) ?? false
|
||||
let presetRaw = try container.decodeIfPresent(Int.self, forKey: .filenameFormatPreset) ?? FilenameFormatPreset.macOSStyle.rawValue
|
||||
filenameFormatPreset = FilenameFormatPreset(rawValue: presetRaw) ?? .macOSStyle
|
||||
thumbnailFixedSize = try container.decodeIfPresent(ThumbnailFixedSize.self, forKey: .thumbnailFixedSize) ?? .medium
|
||||
showFolderButton = try container.decodeIfPresent(Bool.self, forKey: .showFolderButton) ?? true
|
||||
startAppOnLogin = try container.decodeIfPresent(Bool.self, forKey: .startAppOnLogin) ?? false
|
||||
autoSaveScreenshot = try container.decodeIfPresent(Bool.self, forKey: .autoSaveScreenshot) ?? false
|
||||
closeAfterSave = try container.decodeIfPresent(Bool.self, forKey: .closeAfterSave) ?? false
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(screenshotFolder, forKey: .screenshotFolder)
|
||||
try container.encode(filenamePrefix, forKey: .filenamePrefix)
|
||||
try container.encode(filenameFormatPreset.rawValue, forKey: .filenameFormatPreset)
|
||||
try container.encode(filenameCustomFormat, forKey: .filenameCustomFormat)
|
||||
try container.encode(saveAfterEdit, forKey: .saveAfterEdit)
|
||||
try container.encode(playSoundOnCapture, forKey: .playSoundOnCapture)
|
||||
try container.encode(thumbnailTimer, forKey: .thumbnailTimer)
|
||||
try container.encode(closeAfterDrag, forKey: .closeAfterDrag)
|
||||
try container.encode(thumbnailFixedSize, forKey: .thumbnailFixedSize)
|
||||
try container.encode(showFolderButton, forKey: .showFolderButton)
|
||||
try container.encode(startAppOnLogin, forKey: .startAppOnLogin)
|
||||
try container.encode(autoSaveScreenshot, forKey: .autoSaveScreenshot)
|
||||
try container.encode(closeAfterSave, forKey: .closeAfterSave)
|
||||
}
|
||||
|
||||
func resetToDefaults() {
|
||||
objectWillChange.send() // Stuur eenmalig aan het begin
|
||||
|
||||
// Wijs defaults direct toe aan de properties; de didSets zorgen voor opslaan en notificaties.
|
||||
screenshotFolder = defaultScreenshotFolder()
|
||||
filenamePrefix = "Schermafbeelding"
|
||||
filenameFormatPreset = .macOSStyle
|
||||
filenameCustomFormat = "{YYYY}-{MM}-{DD}_{hh}.{mm}.{ss}"
|
||||
saveAfterEdit = false
|
||||
playSoundOnCapture = true
|
||||
thumbnailTimer = 0
|
||||
closeAfterDrag = false
|
||||
thumbnailFixedSize = .medium
|
||||
showFolderButton = true
|
||||
startAppOnLogin = false
|
||||
autoSaveScreenshot = false
|
||||
closeAfterSave = false
|
||||
isRenameActionEnabled = true
|
||||
isStashActionEnabled = true
|
||||
isOCRActionEnabled = true
|
||||
isClipboardActionEnabled = true
|
||||
isCancelActionEnabled = true
|
||||
isRemoveActionEnabled = true
|
||||
isDeleteActionEnabled = true
|
||||
isCancelDragActionEnabled = true
|
||||
stashAlwaysOnTop = false
|
||||
hideDesktopIconsDuringScreenshot = false
|
||||
|
||||
// 🔥💎 MEGA NIEUWE RESET VOOR STASH PREVIEW SIZE! 💎🔥
|
||||
stashPreviewSize = .medium
|
||||
|
||||
// 🔥💥⚡ HYPERMODE STASH GRID RESET! ⚡💥🔥
|
||||
stashGridMode = .fixedColumns
|
||||
stashMaxColumns = 2
|
||||
stashMaxRows = 1
|
||||
|
||||
// 🔥 NIEUW: Reset persistent stash
|
||||
persistentStash = false
|
||||
|
||||
// 🔄 RESET UPDATE SETTINGS
|
||||
automaticUpdates = true
|
||||
includePreReleases = false
|
||||
|
||||
// 🎹 RESET CUSTOM SHORTCUT SETTINGS
|
||||
useCustomShortcut = false
|
||||
customShortcutModifiers = 0
|
||||
customShortcutKey = 0
|
||||
|
||||
|
||||
// Verwijder de saveSettings() aanroep.
|
||||
// Verwijder de individuele NotificationCenter.default.post calls hier; didSets handelen dat af.
|
||||
}
|
||||
|
||||
private func defaultScreenshotFolder() -> String? {
|
||||
// Voorbeeld: probeer ~/Pictures/Screenshots, anders nil
|
||||
if let picturesURL = FileManager.default.urls(for: .picturesDirectory, in: .userDomainMask).first {
|
||||
let screenshotsURL = picturesURL.appendingPathComponent("Screenshots")
|
||||
// Maak de map als hij niet bestaat (optioneel)
|
||||
// try? FileManager.default.createDirectory(at: screenshotsURL, withIntermediateDirectories: true, attributes: nil)
|
||||
return screenshotsURL.path
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resetActionOrder() {
|
||||
actionOrder = ActionType.allCases
|
||||
}
|
||||
|
||||
func moveAction(_ action: ActionType, direction: Int) {
|
||||
guard let currentIndex = actionOrder.firstIndex(of: action) else { return }
|
||||
let newIndex = currentIndex + direction
|
||||
|
||||
guard newIndex >= 0 && newIndex < actionOrder.count else { return }
|
||||
|
||||
actionOrder.swapAt(currentIndex, newIndex)
|
||||
}
|
||||
|
||||
// saveSettings() is waarschijnlijk niet nodig als @Published didSets goed werken.
|
||||
// Als je het toch wilt:
|
||||
/*
|
||||
func saveSettings() {
|
||||
UserDefaults.standard.set(screenshotFolder, forKey: SettingsKey.screenshotFolder)
|
||||
// ... etc. voor alle settings ...
|
||||
UserDefaults.standard.synchronize() // Optioneel, gebeurt periodiek
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// MARK: - Settings Window UI (Nieuwe structuur met TabView)
|
||||
|
||||
struct SettingsSnapshot {
|
||||
var screenshotFolder: String?
|
||||
var thumbnailTimer: Int
|
||||
var closeAfterDrag: Bool
|
||||
var thumbnailFixedSize: ThumbnailFixedSize
|
||||
var showFolderButton: Bool
|
||||
var closeAfterSave: Bool
|
||||
var playSoundOnCapture: Bool
|
||||
var filenamePrefix: String
|
||||
var filenameFormatPreset: FilenameFormatPreset
|
||||
var filenameCustomFormat: String
|
||||
var startAppOnLogin: Bool
|
||||
var autoSaveScreenshot: Bool
|
||||
var thumbnailDisplayScreen: ThumbnailDisplayScreen
|
||||
var stashAlwaysOnTop: Bool
|
||||
var hideDesktopIconsDuringScreenshot: Bool
|
||||
|
||||
static func captureCurrent() -> SettingsSnapshot {
|
||||
let s = SettingsManager.shared
|
||||
return SettingsSnapshot(
|
||||
screenshotFolder: s.screenshotFolder,
|
||||
thumbnailTimer: s.thumbnailTimer,
|
||||
closeAfterDrag: s.closeAfterDrag,
|
||||
thumbnailFixedSize: s.thumbnailFixedSize,
|
||||
showFolderButton: s.showFolderButton,
|
||||
closeAfterSave: s.closeAfterSave,
|
||||
playSoundOnCapture: s.playSoundOnCapture,
|
||||
filenamePrefix: s.filenamePrefix,
|
||||
filenameFormatPreset: s.filenameFormatPreset,
|
||||
filenameCustomFormat: s.filenameCustomFormat,
|
||||
startAppOnLogin: s.startAppOnLogin,
|
||||
autoSaveScreenshot: s.autoSaveScreenshot,
|
||||
thumbnailDisplayScreen: s.thumbnailDisplayScreen,
|
||||
stashAlwaysOnTop: s.stashAlwaysOnTop,
|
||||
hideDesktopIconsDuringScreenshot: s.hideDesktopIconsDuringScreenshot
|
||||
)
|
||||
}
|
||||
|
||||
func apply(to manager: SettingsManager = SettingsManager.shared) {
|
||||
manager.screenshotFolder = screenshotFolder
|
||||
manager.thumbnailTimer = thumbnailTimer
|
||||
manager.closeAfterDrag = closeAfterDrag
|
||||
manager.thumbnailFixedSize = thumbnailFixedSize
|
||||
manager.showFolderButton = showFolderButton
|
||||
manager.closeAfterSave = closeAfterSave
|
||||
manager.playSoundOnCapture = playSoundOnCapture
|
||||
manager.filenamePrefix = filenamePrefix
|
||||
manager.filenameFormatPreset = filenameFormatPreset
|
||||
manager.filenameCustomFormat = filenameCustomFormat
|
||||
manager.startAppOnLogin = startAppOnLogin
|
||||
manager.autoSaveScreenshot = autoSaveScreenshot
|
||||
manager.thumbnailDisplayScreen = thumbnailDisplayScreen
|
||||
manager.stashAlwaysOnTop = stashAlwaysOnTop
|
||||
manager.hideDesktopIconsDuringScreenshot = hideDesktopIconsDuringScreenshot
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Cache Manager
|
||||
struct CacheManager {
|
||||
static let shared = CacheManager()
|
||||
|
||||
private init() {}
|
||||
|
||||
// Get thumbnail directory path
|
||||
private var thumbnailDirectory: URL {
|
||||
let appSupportDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
|
||||
let shotScreenDirectory = appSupportDirectory.appendingPathComponent("ShotScreen")
|
||||
let thumbnailsDirectory = shotScreenDirectory.appendingPathComponent("Thumbnails")
|
||||
return thumbnailsDirectory
|
||||
}
|
||||
|
||||
// Calculate cache size in MB
|
||||
func getCacheSize() -> Double {
|
||||
do {
|
||||
let contents = try FileManager.default.contentsOfDirectory(at: thumbnailDirectory, includingPropertiesForKeys: [.fileSizeKey], options: [])
|
||||
|
||||
var totalSize: Int64 = 0
|
||||
for fileURL in contents {
|
||||
// Skip thumbnail restoration directory from cache size calculation
|
||||
if fileURL.lastPathComponent == "thumbnail_restoration" {
|
||||
continue
|
||||
}
|
||||
|
||||
do {
|
||||
let resourceValues = try fileURL.resourceValues(forKeys: [.fileSizeKey])
|
||||
if let fileSize = resourceValues.fileSize {
|
||||
totalSize += Int64(fileSize)
|
||||
}
|
||||
} catch {
|
||||
print("⚠️ Error getting file size for \(fileURL.lastPathComponent): \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
// Convert bytes to MB
|
||||
return Double(totalSize) / (1024 * 1024)
|
||||
} catch {
|
||||
print("⚠️ Error calculating cache size: \(error)")
|
||||
return 0.0
|
||||
}
|
||||
}
|
||||
|
||||
// Get cache file count
|
||||
func getCacheFileCount() -> Int {
|
||||
do {
|
||||
let contents = try FileManager.default.contentsOfDirectory(at: thumbnailDirectory, includingPropertiesForKeys: [.isDirectoryKey], options: [])
|
||||
|
||||
var fileCount = 0
|
||||
for fileURL in contents {
|
||||
// Skip thumbnail restoration directory
|
||||
if fileURL.lastPathComponent == "thumbnail_restoration" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Only count PNG files, not directories
|
||||
let resourceValues = try? fileURL.resourceValues(forKeys: [.isDirectoryKey])
|
||||
let isDirectory = resourceValues?.isDirectory ?? false
|
||||
|
||||
if !isDirectory && fileURL.pathExtension == "png" {
|
||||
fileCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
return fileCount
|
||||
} catch {
|
||||
print("⚠️ Error getting cache file count: \(error)")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all cache (except active thumbnails)
|
||||
func clearCache(preserveActiveThumbnails: Bool = true) -> (deletedFiles: Int, savedSpace: Double) {
|
||||
do {
|
||||
let contents = try FileManager.default.contentsOfDirectory(at: thumbnailDirectory, includingPropertiesForKeys: [.fileSizeKey, .creationDateKey], options: [])
|
||||
|
||||
var deletedFiles = 0
|
||||
var savedSpace: Int64 = 0
|
||||
let activeThumbnailPath = getActiveThumbnailPath()
|
||||
|
||||
for fileURL in contents {
|
||||
// Skip if this is the active thumbnail and we want to preserve it
|
||||
if preserveActiveThumbnails && fileURL.path == activeThumbnailPath {
|
||||
print("🔒 Preserving active thumbnail: \(fileURL.lastPathComponent)")
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip thumbnail restoration directory completely
|
||||
if fileURL.lastPathComponent == "thumbnail_restoration" {
|
||||
print("🔒 Preserving thumbnail restoration directory: \(fileURL.lastPathComponent)")
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip thumbnail restoration backup files
|
||||
if fileURL.lastPathComponent.contains("latest_backup") {
|
||||
print("🔒 Preserving thumbnail restoration backup file: \(fileURL.lastPathComponent)")
|
||||
continue
|
||||
}
|
||||
|
||||
do {
|
||||
let resourceValues = try fileURL.resourceValues(forKeys: [.fileSizeKey])
|
||||
let fileSize = resourceValues.fileSize ?? 0
|
||||
|
||||
try FileManager.default.removeItem(at: fileURL)
|
||||
deletedFiles += 1
|
||||
savedSpace += Int64(fileSize)
|
||||
print("🗑️ Deleted cache file: \(fileURL.lastPathComponent)")
|
||||
} catch {
|
||||
print("⚠️ Failed to delete \(fileURL.lastPathComponent): \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
let savedSpaceMB = Double(savedSpace) / (1024 * 1024)
|
||||
print("✅ Cache cleanup complete: \(deletedFiles) files deleted, \(String(format: "%.1f", savedSpaceMB)) MB freed")
|
||||
return (deletedFiles, savedSpaceMB)
|
||||
|
||||
} catch {
|
||||
print("❌ Error during cache cleanup: \(error)")
|
||||
return (0, 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
// Clean cache based on retention time
|
||||
func cleanupOldCache() {
|
||||
let retentionTime = SettingsManager.shared.cacheRetentionTime
|
||||
|
||||
// Don't cleanup if retention is set to forever
|
||||
guard let maxAge = retentionTime.timeInterval else {
|
||||
print("🗂️ Cache retention set to forever - no automatic cleanup")
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let contents = try FileManager.default.contentsOfDirectory(at: thumbnailDirectory, includingPropertiesForKeys: [.creationDateKey, .fileSizeKey], options: [])
|
||||
let cutoffDate = Date().addingTimeInterval(-maxAge)
|
||||
let activeThumbnailPath = getActiveThumbnailPath()
|
||||
|
||||
print("🧪 CLEANUP: Checking \(contents.count) files. Cutoff time: \(cutoffDate). Max age: \(Int(maxAge))s")
|
||||
|
||||
var deletedFiles = 0
|
||||
var savedSpace: Int64 = 0
|
||||
var checkedFiles = 0
|
||||
|
||||
for fileURL in contents {
|
||||
// Skip active thumbnail
|
||||
if fileURL.path == activeThumbnailPath {
|
||||
print("🧪 CLEANUP: Skipping active thumbnail: \(fileURL.lastPathComponent)")
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip thumbnail restoration directory completely
|
||||
if fileURL.lastPathComponent == "thumbnail_restoration" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip thumbnail restoration backup files
|
||||
if fileURL.lastPathComponent.contains("latest_backup") {
|
||||
continue
|
||||
}
|
||||
|
||||
do {
|
||||
let resourceValues = try fileURL.resourceValues(forKeys: [.creationDateKey, .fileSizeKey])
|
||||
|
||||
if let creationDate = resourceValues.creationDate {
|
||||
checkedFiles += 1
|
||||
let age = Date().timeIntervalSince(creationDate)
|
||||
print("🧪 CLEANUP: \(fileURL.lastPathComponent) - age: \(Int(age))s, created: \(creationDate)")
|
||||
|
||||
if creationDate < cutoffDate {
|
||||
let fileSize = resourceValues.fileSize ?? 0
|
||||
try FileManager.default.removeItem(at: fileURL)
|
||||
deletedFiles += 1
|
||||
savedSpace += Int64(fileSize)
|
||||
print("🗑️ DELETED: \(fileURL.lastPathComponent) (was \(Int(age))s old)")
|
||||
} else {
|
||||
print("✅ KEEPING: \(fileURL.lastPathComponent) (only \(Int(age))s old)")
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print("⚠️ Error processing \(fileURL.lastPathComponent): \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
if deletedFiles > 0 {
|
||||
let savedSpaceMB = Double(savedSpace) / (1024 * 1024)
|
||||
print("✅ Auto cache cleanup: \(deletedFiles) old files deleted, \(String(format: "%.1f", savedSpaceMB)) MB freed")
|
||||
} else if checkedFiles > 0 {
|
||||
print("🧪 CLEANUP: No files old enough to delete (\(checkedFiles) files checked)")
|
||||
} else {
|
||||
print("🧪 CLEANUP: No cache files found to check")
|
||||
}
|
||||
|
||||
} catch {
|
||||
print("❌ Error during automatic cache cleanup: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
// Get active thumbnail path (to avoid deleting currently open thumbnail)
|
||||
private func getActiveThumbnailPath() -> String? {
|
||||
// Try to get the active thumbnail URL from ScreenshotApp
|
||||
if let app = NSApp.delegate as? ScreenshotApp,
|
||||
let tempURL = app.getTempURL() {
|
||||
return tempURL.path
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user