This commit is contained in:
Alin
2024-06-13 18:14:26 -06:00
parent 72756ad196
commit 8a4a863015
10 changed files with 294 additions and 174 deletions
+4 -4
View File
@@ -568,8 +568,8 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
APP_BUILD = 46; APP_BUILD = 47;
APP_VERSION = 3.7.1; APP_VERSION = 3.7.2;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
@@ -638,8 +638,8 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
APP_BUILD = 46; APP_BUILD = 47;
APP_VERSION = 3.7.1; APP_VERSION = 3.7.2;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+27 -1
View File
@@ -928,7 +928,7 @@ extension View {
} }
} }
// Background color/glass setter
@ViewBuilder @ViewBuilder
func backgroundView(themeSettings: ThemeSettings, darker: Bool = false, glass: Bool = false) -> some View { func backgroundView(themeSettings: ThemeSettings, darker: Bool = false, glass: Bool = false) -> some View {
if glass { if glass {
@@ -940,6 +940,32 @@ func backgroundView(themeSettings: ThemeSettings, darker: Bool = false, glass: B
} }
struct PickerModifier: ViewModifier {
@State private var isHovered: Bool = false
let themeSettings: ThemeSettings
func body(content: Content) -> some View {
content
.buttonStyle(.borderless)
.padding(4)
.background {
backgroundView(themeSettings: themeSettings, darker: isHovered)
.clipShape(RoundedRectangle(cornerRadius: 10))
}
.onHover { hovering in
isHovered = hovering
}
}
}
extension View {
func pickerStyle(themeSettings: ThemeSettings) -> some View {
self.modifier(PickerModifier(themeSettings: themeSettings))
}
}
// Preset view elements
struct PresetColor: ButtonStyle { struct PresetColor: ButtonStyle {
var fillColor: Color var fillColor: Color
var label: String var label: String
+90 -17
View File
@@ -170,35 +170,62 @@ func UnzipAndReplace(DownloadedFileURL fileURL: String, appState: AppState) {
// --- Updater check frequency // --- Updater check frequency
func updateNextUpdateDate() { enum UpdateFrequency: String, CaseIterable, Identifiable {
@AppStorage("settings.updater.updateTimeframe") var updateTimeframe: Int = 1 case none = "Never"
@AppStorage("settings.updater.nextUpdateDate") var nextUpdateDate = Date.now.timeIntervalSinceReferenceDate case daily = "Daily"
let updateSeconds = updateTimeframe.daysToSeconds case weekly = "Weekly"
let newUpdateDate = Calendar.current.startOfDay(for: Date().addingTimeInterval(updateSeconds)) case monthly = "Monthly"
nextUpdateDate = newUpdateDate.timeIntervalSinceReferenceDate
var id: String { self.rawValue }
var interval: TimeInterval? {
switch self {
case .none:
return nil
case .daily:
return 86400 // 1 day in seconds
case .weekly:
return 604800 // 7 days in seconds
case .monthly:
return 2592000 // 30 days in seconds
}
} }
func updateNextUpdateDate() {
guard let updateInterval = self.interval else { return }
let newUpdateDate = Calendar.current.startOfDay(for: Date().addingTimeInterval(updateInterval))
UserDefaults.standard.set(newUpdateDate.timeIntervalSinceReferenceDate, forKey: "settings.updater.nextUpdateDate")
}
}
//func updateNextUpdateDate() {
// @AppStorage("settings.updater.updateFrequency") var updateFrequency: UpdateFrequency = .daily
// @AppStorage("settings.updater.nextUpdateDate") var nextUpdateDate = Date.now.timeIntervalSinceReferenceDate
//
// guard let updateInterval = updateFrequency.interval else { return }
// let newUpdateDate = Calendar.current.startOfDay(for: Date().addingTimeInterval(updateInterval))
// nextUpdateDate = newUpdateDate.timeIntervalSinceReferenceDate
//}
func checkAndUpdateIfNeeded(appState: AppState) { func checkAndUpdateIfNeeded(appState: AppState) {
@AppStorage("settings.updater.updateTimeframe") var updateTimeframe: Int = 1 @AppStorage("settings.updater.updateFrequency") var updateFrequency: UpdateFrequency = .daily
@AppStorage("settings.updater.nextUpdateDate") var nextUpdateDate = Date.now.timeIntervalSinceReferenceDate @AppStorage("settings.updater.nextUpdateDate") var nextUpdateDate = Date.now.timeIntervalSinceReferenceDate
let updateSeconds = updateTimeframe.daysToSeconds guard let updateInterval = updateFrequency.interval else {
printOS("Updater: no update frequency set, skipping check")
return
}
let now = Date() let now = Date()
// Retrieve the next update date from UserDefaults
let nextUpdateDateLocal = Date(timeIntervalSinceReferenceDate: nextUpdateDate) let nextUpdateDateLocal = Date(timeIntervalSinceReferenceDate: nextUpdateDate)
// let nextUpdateDate = UserDefaults.standard.object(forKey: "settings.updater.nextUpdateDate") as? Date
// If there's no stored next update date or it's in the past, update immediately
if !isSameDay(date1: nextUpdateDateLocal, date2: now) { if !isSameDay(date1: nextUpdateDateLocal, date2: now) {
// Next update date is in the future, no need to update printOS("Updater: next update date is in the future, skipping (\(nextUpdateDateLocal))")
printOS("Updater: next update date is in the future, skipping")
return return
} }
// Update immediately and set next update date
updateApp(appState: appState) updateApp(appState: appState)
setNextUpdateDate(interval: updateSeconds) setNextUpdateDate(interval: updateInterval)
} }
func updateApp(appState: AppState) { func updateApp(appState: AppState) {
@@ -210,13 +237,59 @@ func updateApp(appState: AppState) {
func setNextUpdateDate(interval: TimeInterval) { func setNextUpdateDate(interval: TimeInterval) {
let newUpdateDate = Calendar.current.startOfDay(for: Date().addingTimeInterval(interval)) let newUpdateDate = Calendar.current.startOfDay(for: Date().addingTimeInterval(interval))
UserDefaults.standard.set(newUpdateDate.timeIntervalSinceReferenceDate, forKey: "settings.updater.nextUpdateDate") UserDefaults.standard.set(newUpdateDate.timeIntervalSinceReferenceDate, forKey: "settings.updater.nextUpdateDate")
// UserDefaults.standard.set(newUpdateDate, forKey: "settings.updater.nextUpdateDate")
} }
func isSameDay(date1: Date, date2: Date) -> Bool { func isSameDay(date1: Date, date2: Date) -> Bool {
return Calendar.current.isDate(date1, inSameDayAs: date2) return Calendar.current.isDate(date1, inSameDayAs: date2)
} }
//func updateNextUpdateDate() {
// @AppStorage("settings.updater.updateTimeframe") var updateTimeframe: Int = 1
// @AppStorage("settings.updater.nextUpdateDate") var nextUpdateDate = Date.now.timeIntervalSinceReferenceDate
// let updateSeconds = updateTimeframe.daysToSeconds
// let newUpdateDate = Calendar.current.startOfDay(for: Date().addingTimeInterval(updateSeconds))
// nextUpdateDate = newUpdateDate.timeIntervalSinceReferenceDate
//}
//
//func checkAndUpdateIfNeeded(appState: AppState) {
// @AppStorage("settings.updater.updateTimeframe") var updateTimeframe: Int = 1
// @AppStorage("settings.updater.nextUpdateDate") var nextUpdateDate = Date.now.timeIntervalSinceReferenceDate
//
// let updateSeconds = updateTimeframe.daysToSeconds
// let now = Date()
//
// // Retrieve the next update date from UserDefaults
// let nextUpdateDateLocal = Date(timeIntervalSinceReferenceDate: nextUpdateDate)
//// let nextUpdateDate = UserDefaults.standard.object(forKey: "settings.updater.nextUpdateDate") as? Date
//
// // If there's no stored next update date or it's in the past, update immediately
// if !isSameDay(date1: nextUpdateDateLocal, date2: now) {
// // Next update date is in the future, no need to update
// printOS("Updater: next update date is in the future, skipping")
// return
// }
//
// // Update immediately and set next update date
// updateApp(appState: appState)
// setNextUpdateDate(interval: updateSeconds)
//}
//
//func updateApp(appState: AppState) {
// // Perform your update logic here
// printOS("Updater: performing update")
// loadGithubReleases(appState: appState)
//}
//
//func setNextUpdateDate(interval: TimeInterval) {
// let newUpdateDate = Calendar.current.startOfDay(for: Date().addingTimeInterval(interval))
// UserDefaults.standard.set(newUpdateDate.timeIntervalSinceReferenceDate, forKey: "settings.updater.nextUpdateDate")
//// UserDefaults.standard.set(newUpdateDate, forKey: "settings.updater.nextUpdateDate")
//}
//
//func isSameDay(date1: Date, date2: Date) -> Bool {
// return Calendar.current.isDate(date1, inSameDayAs: date2)
//}
// --- Updater Badge View // --- Updater Badge View
+9 -8
View File
@@ -15,8 +15,7 @@ struct PearcleanerApp: App {
@StateObject var locations = Locations() @StateObject var locations = Locations()
@StateObject var fsm = FolderSettingsManager() @StateObject var fsm = FolderSettingsManager()
@State private var windowSettings = WindowSettings() @State private var windowSettings = WindowSettings()
// @AppStorage("settings.updater.updateTimeframe") private var updateTimeframe: Int = 1 @AppStorage("settings.updater.updateFrequency") private var updateFrequency: UpdateFrequency = .daily
@AppStorage("settings.updater.enableUpdates") private var enableUpdates: Bool = true
@AppStorage("settings.permissions.hasLaunched") private var hasLaunched: Bool = false @AppStorage("settings.permissions.hasLaunched") private var hasLaunched: Bool = false
@AppStorage("displayMode") var displayMode: DisplayMode = .system @AppStorage("displayMode") var displayMode: DisplayMode = .system
@AppStorage("settings.general.mini") private var mini: Bool = false @AppStorage("settings.general.mini") private var mini: Bool = false
@@ -121,7 +120,7 @@ struct PearcleanerApp: App {
// Get GH releases // Get GH releases
loadGithubReleases(appState: appState, releaseOnly: true) loadGithubReleases(appState: appState, releaseOnly: true)
if enableUpdates { if updateFrequency != .none {
// Update checker // Update checker
checkAndUpdateIfNeeded(appState: appState) checkAndUpdateIfNeeded(appState: appState)
} }
@@ -181,9 +180,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
findAndSetWindowFrame(named: ["Pearcleaner"], windowSettings: windowSettings) findAndSetWindowFrame(named: ["Pearcleaner"], windowSettings: windowSettings)
if UserDefaults.standard.object(forKey: "themeColor") == nil { // if UserDefaults.standard.object(forKey: "themeColor") == nil {
self.appearanceChanged() // self.appearanceChanged()
} // }
self.appearanceCheck()
if menubarEnabled { if menubarEnabled {
findAndHideWindows(named: ["Pearcleaner"]) findAndHideWindows(named: ["Pearcleaner"])
@@ -194,14 +195,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
observer = DistributedNotificationCenter.default().addObserver(forName: NSNotification.Name(rawValue: "AppleInterfaceThemeChangedNotification"), object: nil, queue: OperationQueue.main) { [weak self] _ in 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") let themeMode = UserDefaults.standard.string(forKey: "settings.general.selectedTheme")
if themeMode == "Auto" { if themeMode == "Auto" {
self?.appearanceChanged() self?.appearanceCheck()
} }
} }
} }
func appearanceChanged() { func appearanceCheck() {
let dm = UserDefaults.standard.integer(forKey: "displayMode") let dm = UserDefaults.standard.integer(forKey: "displayMode")
var displayMode = DisplayMode(rawValue: dm) var displayMode = DisplayMode(rawValue: dm)
let dark = isDarkMode() let dark = isDarkMode()
+5 -2
View File
@@ -20,6 +20,7 @@ struct FolderSettingsTab: View {
@EnvironmentObject var appState: AppState @EnvironmentObject var appState: AppState
@EnvironmentObject var locations: Locations @EnvironmentObject var locations: Locations
@EnvironmentObject var fsm: FolderSettingsManager @EnvironmentObject var fsm: FolderSettingsManager
@EnvironmentObject var themeSettings: ThemeSettings
@State private var isHovered = false @State private var isHovered = false
var body: some View { var body: some View {
@@ -81,7 +82,8 @@ struct FolderSettingsTab: View {
} }
.scrollIndicators(.automatic) .scrollIndicators(.automatic)
.padding() .padding()
.background(Color("mode").opacity(0.05)) // .background(Color("mode").opacity(0.05))
.background(backgroundView(themeSettings: themeSettings, darker: true))
.clipShape(RoundedRectangle(cornerRadius: 10)) .clipShape(RoundedRectangle(cornerRadius: 10))
.onDrop(of: ["public.file-url"], isTargeted: nil) { providers -> Bool in .onDrop(of: ["public.file-url"], isTargeted: nil) { providers -> Bool in
providers.forEach { provider in providers.forEach { provider in
@@ -173,7 +175,8 @@ struct FolderSettingsTab: View {
} }
.scrollIndicators(.automatic) .scrollIndicators(.automatic)
.padding() .padding()
.background(Color("mode").opacity(0.05)) .background(backgroundView(themeSettings: themeSettings, darker: true))
// .background(Color("mode").opacity(0.05))
.clipShape(RoundedRectangle(cornerRadius: 10)) .clipShape(RoundedRectangle(cornerRadius: 10))
.onDrop(of: ["public.file-url"], isTargeted: nil) { providers -> Bool in .onDrop(of: ["public.file-url"], isTargeted: nil) { providers -> Bool in
providers.forEach { provider in providers.forEach { provider in
+23 -65
View File
@@ -13,6 +13,7 @@ import FinderSync
struct GeneralSettingsTab: View { struct GeneralSettingsTab: View {
@EnvironmentObject var appState: AppState @EnvironmentObject var appState: AppState
@EnvironmentObject var locations: Locations @EnvironmentObject var locations: Locations
@EnvironmentObject var themeSettings: ThemeSettings
@State private var windowSettings = WindowSettings() @State private var windowSettings = WindowSettings()
@AppStorage("settings.general.glass") private var glass: Bool = true @AppStorage("settings.general.glass") private var glass: Bool = true
@AppStorage("settings.general.mini") private var mini: Bool = false @AppStorage("settings.general.mini") private var mini: Bool = false
@@ -107,31 +108,13 @@ struct GeneralSettingsTab: View {
} }
InfoButton(text: "When searching for app files or leftover files, the list will be sorted either alphabetically or by size(large to small)") InfoButton(text: "When searching for app files or leftover files, the list will be sorted either alphabetically or by size(large to small)")
Spacer() Spacer()
SegmentedPicker( Picker("", selection: $selectedSortAlpha) {
["Alpha", "Size"], Text("Alpha")
selectedIndex: Binding( .tag(true)
get: { selectedSortAlpha ? 0 : 1 }, Text("Size")
set: { newIndex in .tag(false)
withAnimation(.easeInOut(duration: 0.3)) {
selectedSortAlpha = (newIndex == 0)
} }
}), .pickerStyle(themeSettings: themeSettings)
selectionAlignment: .bottom,
content: { item, isSelected in
Text(item)
.font(.callout)
.foregroundColor(isSelected ? Color("mode") : Color("mode").opacity(0.5))
.padding(.horizontal)
.padding(.bottom, 5)
.frame(width: 75)
},
selection: {
VStack(spacing: 0) {
Spacer()
Color("pear").frame(height: 1)
}
})
} }
.padding(5) .padding(5)
.padding(.leading) .padding(.leading)
@@ -145,48 +128,23 @@ struct GeneralSettingsTab: View {
.padding(.trailing) .padding(.trailing)
.foregroundStyle(Color("mode").opacity(0.5)) .foregroundStyle(Color("mode").opacity(0.5))
VStack(alignment: .leading, spacing: 5) { VStack(alignment: .leading, spacing: 5) {
Text("File size display") Text("File size display mode")
.font(.callout) .font(.callout)
.foregroundStyle(Color("mode").opacity(0.5)) .foregroundStyle(Color("mode").opacity(0.5))
} }
InfoButton(text: "Real size type will show how much actual allocated space the file has on disk. Logical type shows the binary size. The filesystem can compress and deduplicate sectors on disk, so real size is sometimes smaller(or bigger) than logical size. Finder size is similar to if you right click > Get Info on a file in Finder, which will show both the logical and real sizes together.") InfoButton(text: "Real size type will show how much actual allocated space the file has on disk. Logical type shows the binary size. The filesystem can compress and deduplicate sectors on disk, so real size is sometimes smaller(or bigger) than logical size. Finder size is similar to if you right click > Get Info on a file in Finder, which will show both the logical and real sizes together.")
Spacer() Spacer()
SegmentedPicker( Picker("", selection: $sizeType) {
["Real", "Logical", "Finder"], Text("Real")
selectedIndex: Binding( .tag("Real")
get: { Text("Logical")
switch sizeType { .tag("Logical")
case "Real": return 0 Text("Finder")
case "Logical": return 1 .tag("Finder")
case "Finder": return 2
default: return 0
} }
}, .pickerStyle(themeSettings: themeSettings)
set: { newIndex in
withAnimation(.easeInOut(duration: 0.3)) {
switch newIndex {
case 0: sizeType = "Real"
case 1: sizeType = "Logical"
case 2: sizeType = "Finder"
default: sizeType = "Real"
}
}
}),
selectionAlignment: .bottom,
content: { item, isSelected in
Text(item)
.font(.callout)
.foregroundColor(isSelected ? Color("mode") : Color("mode").opacity(0.5) )
.padding(.horizontal)
.padding(.bottom, 5)
.frame(width: 75)
},
selection: {
VStack(spacing: 0) {
Spacer()
Color("pear").frame(height: 1)
}
})
} }
.padding(5) .padding(5)
.padding(.leading) .padding(.leading)
@@ -237,7 +195,7 @@ struct GeneralSettingsTab: View {
.frame(width: 20, height: 20) .frame(width: 20, height: 20)
.padding(.trailing) .padding(.trailing)
.foregroundStyle(diskStatus ? .green : .red) .foregroundStyle(diskStatus ? .green : .red)
.saturation(displayMode.colorScheme == .dark ? 0.5 : 1) .saturation(displayMode.colorScheme == .dark ? 0.8 : 1)
Text(diskStatus ? "Full Disk permission granted" : "Full Disk permission not granted") Text(diskStatus ? "Full Disk permission granted" : "Full Disk permission not granted")
.font(.callout) .font(.callout)
.foregroundStyle(Color("mode").opacity(0.5)) .foregroundStyle(Color("mode").opacity(0.5))
@@ -265,7 +223,7 @@ struct GeneralSettingsTab: View {
.frame(width: 20, height: 20) .frame(width: 20, height: 20)
.padding(.trailing) .padding(.trailing)
.foregroundStyle(accessStatus ? .green : .red) .foregroundStyle(accessStatus ? .green : .red)
.saturation(displayMode.colorScheme == .dark ? 0.5 : 1) .saturation(displayMode.colorScheme == .dark ? 0.8 : 1)
Text(accessStatus ? "Accessibility permission granted" : "Accessibility permission not granted") Text(accessStatus ? "Accessibility permission granted" : "Accessibility permission not granted")
.font(.callout) .font(.callout)
.foregroundStyle(Color("mode").opacity(0.5)) .foregroundStyle(Color("mode").opacity(0.5))
@@ -292,7 +250,7 @@ struct GeneralSettingsTab: View {
.frame(width: 20, height: 20) .frame(width: 20, height: 20)
.padding(.trailing) .padding(.trailing)
.foregroundStyle(autoStatus ? .green : .red) .foregroundStyle(autoStatus ? .green : .red)
.saturation(displayMode.colorScheme == .dark ? 0.5 : 1) .saturation(displayMode.colorScheme == .dark ? 0.8 : 1)
Text(autoStatus ? "Automation permission granted" : "Automation permission not granted") Text(autoStatus ? "Automation permission granted" : "Automation permission not granted")
.font(.callout) .font(.callout)
.foregroundStyle(Color("mode").opacity(0.5)) .foregroundStyle(Color("mode").opacity(0.5))
@@ -330,7 +288,7 @@ struct GeneralSettingsTab: View {
.frame(width: 20, height: 20) .frame(width: 20, height: 20)
.padding(.trailing) .padding(.trailing)
.foregroundStyle(sentinel ? .green : .red) .foregroundStyle(sentinel ? .green : .red)
.saturation(displayMode.colorScheme == .dark ? 0.5 : 1) .saturation(displayMode.colorScheme == .dark ? 0.8 : 1)
Text(sentinel ? "Detecting when apps are moved to Trash" : "**NOT** detecting when apps are moved to Trash") Text(sentinel ? "Detecting when apps are moved to Trash" : "**NOT** detecting when apps are moved to Trash")
.font(.callout) .font(.callout)
.foregroundStyle(Color("mode").opacity(0.5)) .foregroundStyle(Color("mode").opacity(0.5))
@@ -371,7 +329,7 @@ struct GeneralSettingsTab: View {
.frame(width: 20, height: 20) .frame(width: 20, height: 20)
.padding(.trailing) .padding(.trailing)
.foregroundStyle(appState.finderExtensionEnabled ? .green : .red) .foregroundStyle(appState.finderExtensionEnabled ? .green : .red)
.saturation(displayMode.colorScheme == .dark ? 0.5 : 1) .saturation(displayMode.colorScheme == .dark ? 0.8 : 1)
Text(appState.finderExtensionEnabled ? "Context menu extension for Finder is enabled" : "Context menu extension for Finder is disabled") Text(appState.finderExtensionEnabled ? "Context menu extension for Finder is enabled" : "Context menu extension for Finder is disabled")
.font(.callout) .font(.callout)
.foregroundStyle(Color("mode").opacity(0.5)) .foregroundStyle(Color("mode").opacity(0.5))
+8 -33
View File
@@ -193,40 +193,15 @@ struct InterfaceSettingsTab: View {
} }
InfoButton(text: "Changing the color mode will reset the base color to defaults") InfoButton(text: "Changing the color mode will reset the base color to defaults")
Spacer() Spacer()
SegmentedPicker( Picker("", selection: $selectedTheme) {
["Auto", "Dark", "Light"], Text("Auto")
selectedIndex: Binding( .tag("Auto")
get: { Text("Dark")
switch selectedTheme { .tag("Dark")
case "Dark": return 1 Text("Light")
case "Light": return 2 .tag("Light")
default: return 0
} }
}, .pickerStyle(themeSettings: themeSettings)
set: { newIndex in
withAnimation(.easeInOut(duration: 0.3)) {
switch newIndex {
case 1: selectedTheme = "Dark"
case 2: selectedTheme = "Light"
default: selectedTheme = "Auto"
}
}
}),
selectionAlignment: .bottom,
content: { item, isSelected in
Text(item)
.font(.callout)
.foregroundColor(isSelected ? Color("mode") : Color("mode").opacity(0.5) )
.padding(.horizontal)
.padding(.bottom, 5)
.frame(width: 65)
},
selection: {
VStack(spacing: 0) {
Spacer()
Color("pear").frame(height: 1)
}
})
.onChange(of: selectedTheme) { newTheme in .onChange(of: selectedTheme) { newTheme in
switch newTheme { switch newTheme {
case "Auto": case "Auto":
+20 -38
View File
@@ -11,44 +11,36 @@ import Foundation
struct UpdateSettingsTab: View { struct UpdateSettingsTab: View {
@EnvironmentObject var appState: AppState @EnvironmentObject var appState: AppState
@EnvironmentObject var themeSettings: ThemeSettings
@State private var showAlert = false @State private var showAlert = false
@State private var showDone = false @State private var showDone = false
@AppStorage("settings.updater.nextUpdateDate") private var nextUpdateDate = Date.now.timeIntervalSinceReferenceDate @AppStorage("settings.updater.nextUpdateDate") private var nextUpdateDate = Date.now.timeIntervalSinceReferenceDate
@AppStorage("settings.updater.updateTimeframe") private var updateTimeframe: Int = 1 @AppStorage("settings.updater.updateFrequency") private var updateFrequency: UpdateFrequency = .daily
@AppStorage("settings.updater.enableUpdates") private var enableUpdates: Bool = true
var body: some View { var body: some View {
VStack { VStack {
HStack(spacing: 0) {
Image(systemName: "arrow.down.square")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.trailing)
.foregroundStyle(Color("mode").opacity(0.5))
VStack { VStack {
HStack(spacing: 0) { HStack(spacing: 0) {
Text("\(enableUpdates ? "Pearcleaner will check for updates every " : "Automatic updates are disabled")")
Text("Pearcleaner will check for updates")
.font(.callout) .font(.callout)
.foregroundStyle(Color("mode").opacity(0.5)) .foregroundStyle(Color("mode").opacity(0.5))
if enableUpdates {
Text("**\(updateTimeframe)**").font(.system(.callout, design: .monospaced)).monospacedDigit()
Text(updateTimeframe == 1 ? " day" : " days")
.font(.callout)
.foregroundStyle(Color("mode").opacity(0.5))
Stepper("", value: $updateTimeframe, in: 0...30)
.onChange(of: updateTimeframe, perform: { _ in
updateNextUpdateDate()
})
}
Spacer() Spacer()
Picker("", selection: $updateFrequency) {
ForEach(UpdateFrequency.allCases, id: \.self) { frequency in
Text(frequency.rawValue).tag(frequency)
}
}
.onChange(of: updateFrequency) { frequency in
updateFrequency.updateNextUpdateDate()
}
.pickerStyle(themeSettings: themeSettings)
} }
if enableUpdates { if updateFrequency != .none {
HStack { HStack {
Text("Next update check: \(formattedDate(Date(timeIntervalSinceReferenceDate: nextUpdateDate)))") Text("Next update check: \(formattedDate(Date(timeIntervalSinceReferenceDate: nextUpdateDate)))")
.font(.footnote) .font(.footnote)
@@ -56,18 +48,9 @@ struct UpdateSettingsTab: View {
Spacer() Spacer()
} }
} }
}
Spacer()
Toggle(isOn: $enableUpdates, label: {
})
.toggleStyle(.switch)
} }
.padding(5) .padding(5)
.padding(.leading) .padding(.horizontal)
ScrollView { ScrollView {
VStack() { VStack() {
@@ -83,7 +66,10 @@ struct UpdateSettingsTab: View {
} }
.frame(minHeight: 0, maxHeight: .infinity) .frame(minHeight: 0, maxHeight: .infinity)
.frame(minWidth: 0, maxWidth: .infinity) .frame(minWidth: 0, maxWidth: .infinity)
.padding() // .background(Color("mode").opacity(0.05))
.background(backgroundView(themeSettings: themeSettings, darker: true))
.clipShape(RoundedRectangle(cornerRadius: 8))
.padding(.bottom)
Text("Showing last 3 releases") Text("Showing last 3 releases")
.font(.callout) .font(.callout)
@@ -129,10 +115,6 @@ struct UpdateSettingsTab: View {
} }
.padding(20) .padding(20)
.frame(width: 500, height: 520) .frame(width: 500, height: 520)
// .onAppear {
// // Convert TimeInterval to Date on appearance
// let _ = Date(timeIntervalSinceReferenceDate: nextUpdateDate)
// }
} }
} }
+1 -1
View File
@@ -79,7 +79,7 @@ struct AppListItems: View {
} }
if bundleSize == 0 { if bundleSize == 0 {
ProgressView().controlSize(.mini) ProgressView().controlSize(.mini).padding(.leading, 5)
} else { } else {
Text("\(isHovered ? "v\(appInfo.appVersion)" : formatByte(size: bundleSize).human)") Text("\(isHovered ? "v\(appInfo.appVersion)" : formatByte(size: bundleSize).human)")
.font(.system(size: (isHovered || isSelected) ? 12 : 10)) .font(.system(size: (isHovered || isSelected) ? 12 : 10))
@@ -107,3 +107,105 @@ extension View {
} }
} }
} }
// SegmentedPicker(
// ["Alpha", "Size"],
// selectedIndex: Binding(
// get: { selectedSortAlpha ? 0 : 1 },
// set: { newIndex in
// withAnimation(.easeInOut(duration: 0.3)) {
// selectedSortAlpha = (newIndex == 0)
// }
// }),
// selectionAlignment: .bottom,
// content: { item, isSelected in
// Text(item)
// .font(.callout)
// .foregroundColor(isSelected ? Color("mode") : Color("mode").opacity(0.5))
// .padding(.horizontal)
// .padding(.bottom, 5)
// .frame(width: 75)
//
// },
// selection: {
// VStack(spacing: 0) {
// Spacer()
// Color("pear").frame(height: 1)
// }
// })
// SegmentedPicker(
// ["Real", "Logical", "Finder"],
// selectedIndex: Binding(
// get: {
// switch sizeType {
// case "Real": return 0
// case "Logical": return 1
// case "Finder": return 2
// default: return 0
// }
// },
// set: { newIndex in
// withAnimation(.easeInOut(duration: 0.3)) {
// switch newIndex {
// case 0: sizeType = "Real"
// case 1: sizeType = "Logical"
// case 2: sizeType = "Finder"
// default: sizeType = "Real"
// }
// }
// }),
// selectionAlignment: .bottom,
// content: { item, isSelected in
// Text(item)
// .font(.callout)
// .foregroundColor(isSelected ? Color("mode") : Color("mode").opacity(0.5) )
// .padding(.horizontal)
// .padding(.bottom, 5)
// .frame(width: 75)
// },
// selection: {
// VStack(spacing: 0) {
// Spacer()
// Color("pear").frame(height: 1)
// }
// })
// SegmentedPicker(
// ["Auto", "Dark", "Light"],
// selectedIndex: Binding(
// get: {
// switch selectedTheme {
// case "Dark": return 1
// case "Light": return 2
// default: return 0
// }
// },
// set: { newIndex in
// withAnimation(.easeInOut(duration: 0.3)) {
// switch newIndex {
// case 1: selectedTheme = "Dark"
// case 2: selectedTheme = "Light"
// default: selectedTheme = "Auto"
// }
// }
// }),
// selectionAlignment: .bottom,
// content: { item, isSelected in
// Text(item)
// .font(.callout)
// .foregroundColor(isSelected ? Color("mode") : Color("mode").opacity(0.5) )
// .padding(.horizontal)
// .padding(.bottom, 5)
// .frame(width: 65)
// },
// selection: {
// VStack(spacing: 0) {
// Spacer()
// Color("pear").frame(height: 1)
// }
// })