mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 07:19:04 +00:00
v3.7.2
This commit is contained in:
@@ -928,7 +928,7 @@ extension View {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Background color/glass setter
|
||||
@ViewBuilder
|
||||
func backgroundView(themeSettings: ThemeSettings, darker: Bool = false, glass: Bool = false) -> some View {
|
||||
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 {
|
||||
var fillColor: Color
|
||||
var label: String
|
||||
|
||||
@@ -170,35 +170,62 @@ func UnzipAndReplace(DownloadedFileURL fileURL: String, appState: AppState) {
|
||||
|
||||
// --- Updater check frequency
|
||||
|
||||
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
|
||||
enum UpdateFrequency: String, CaseIterable, Identifiable {
|
||||
case none = "Never"
|
||||
case daily = "Daily"
|
||||
case weekly = "Weekly"
|
||||
case monthly = "Monthly"
|
||||
|
||||
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) {
|
||||
@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
|
||||
|
||||
let updateSeconds = updateTimeframe.daysToSeconds
|
||||
guard let updateInterval = updateFrequency.interval else {
|
||||
printOS("Updater: no update frequency set, skipping check")
|
||||
return
|
||||
}
|
||||
|
||||
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")
|
||||
printOS("Updater: next update date is in the future, skipping (\(nextUpdateDateLocal))")
|
||||
return
|
||||
}
|
||||
|
||||
// Update immediately and set next update date
|
||||
updateApp(appState: appState)
|
||||
setNextUpdateDate(interval: updateSeconds)
|
||||
setNextUpdateDate(interval: updateInterval)
|
||||
}
|
||||
|
||||
func updateApp(appState: AppState) {
|
||||
@@ -210,13 +237,59 @@ func updateApp(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)
|
||||
}
|
||||
|
||||
//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
|
||||
|
||||
@@ -15,8 +15,7 @@ struct PearcleanerApp: App {
|
||||
@StateObject var locations = Locations()
|
||||
@StateObject var fsm = FolderSettingsManager()
|
||||
@State private var windowSettings = WindowSettings()
|
||||
// @AppStorage("settings.updater.updateTimeframe") private var updateTimeframe: Int = 1
|
||||
@AppStorage("settings.updater.enableUpdates") private var enableUpdates: Bool = true
|
||||
@AppStorage("settings.updater.updateFrequency") private var updateFrequency: UpdateFrequency = .daily
|
||||
@AppStorage("settings.permissions.hasLaunched") private var hasLaunched: Bool = false
|
||||
@AppStorage("displayMode") var displayMode: DisplayMode = .system
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
@@ -121,7 +120,7 @@ struct PearcleanerApp: App {
|
||||
// Get GH releases
|
||||
loadGithubReleases(appState: appState, releaseOnly: true)
|
||||
|
||||
if enableUpdates {
|
||||
if updateFrequency != .none {
|
||||
// Update checker
|
||||
checkAndUpdateIfNeeded(appState: appState)
|
||||
}
|
||||
@@ -181,10 +180,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
|
||||
|
||||
findAndSetWindowFrame(named: ["Pearcleaner"], windowSettings: windowSettings)
|
||||
|
||||
if UserDefaults.standard.object(forKey: "themeColor") == nil {
|
||||
self.appearanceChanged()
|
||||
}
|
||||
// if UserDefaults.standard.object(forKey: "themeColor") == nil {
|
||||
// self.appearanceChanged()
|
||||
// }
|
||||
|
||||
self.appearanceCheck()
|
||||
|
||||
if menubarEnabled {
|
||||
findAndHideWindows(named: ["Pearcleaner"])
|
||||
NSApplication.shared.setActivationPolicy(.accessory)
|
||||
@@ -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
|
||||
let themeMode = UserDefaults.standard.string(forKey: "settings.general.selectedTheme")
|
||||
if themeMode == "Auto" {
|
||||
self?.appearanceChanged()
|
||||
self?.appearanceCheck()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
func appearanceChanged() {
|
||||
func appearanceCheck() {
|
||||
let dm = UserDefaults.standard.integer(forKey: "displayMode")
|
||||
var displayMode = DisplayMode(rawValue: dm)
|
||||
let dark = isDarkMode()
|
||||
|
||||
@@ -20,6 +20,7 @@ struct FolderSettingsTab: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var locations: Locations
|
||||
@EnvironmentObject var fsm: FolderSettingsManager
|
||||
@EnvironmentObject var themeSettings: ThemeSettings
|
||||
@State private var isHovered = false
|
||||
|
||||
var body: some View {
|
||||
@@ -81,7 +82,8 @@ struct FolderSettingsTab: View {
|
||||
}
|
||||
.scrollIndicators(.automatic)
|
||||
.padding()
|
||||
.background(Color("mode").opacity(0.05))
|
||||
// .background(Color("mode").opacity(0.05))
|
||||
.background(backgroundView(themeSettings: themeSettings, darker: true))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||
.onDrop(of: ["public.file-url"], isTargeted: nil) { providers -> Bool in
|
||||
providers.forEach { provider in
|
||||
@@ -173,7 +175,8 @@ struct FolderSettingsTab: View {
|
||||
}
|
||||
.scrollIndicators(.automatic)
|
||||
.padding()
|
||||
.background(Color("mode").opacity(0.05))
|
||||
.background(backgroundView(themeSettings: themeSettings, darker: true))
|
||||
// .background(Color("mode").opacity(0.05))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||
.onDrop(of: ["public.file-url"], isTargeted: nil) { providers -> Bool in
|
||||
providers.forEach { provider in
|
||||
|
||||
@@ -13,6 +13,7 @@ import FinderSync
|
||||
struct GeneralSettingsTab: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var locations: Locations
|
||||
@EnvironmentObject var themeSettings: ThemeSettings
|
||||
@State private var windowSettings = WindowSettings()
|
||||
@AppStorage("settings.general.glass") private var glass: Bool = true
|
||||
@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)")
|
||||
Spacer()
|
||||
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)
|
||||
}
|
||||
})
|
||||
Picker("", selection: $selectedSortAlpha) {
|
||||
Text("Alpha")
|
||||
.tag(true)
|
||||
Text("Size")
|
||||
.tag(false)
|
||||
}
|
||||
.pickerStyle(themeSettings: themeSettings)
|
||||
}
|
||||
.padding(5)
|
||||
.padding(.leading)
|
||||
@@ -145,48 +128,23 @@ struct GeneralSettingsTab: View {
|
||||
.padding(.trailing)
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
Text("File size display")
|
||||
Text("File size display mode")
|
||||
.font(.callout)
|
||||
.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.")
|
||||
Spacer()
|
||||
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)
|
||||
}
|
||||
})
|
||||
Picker("", selection: $sizeType) {
|
||||
Text("Real")
|
||||
.tag("Real")
|
||||
Text("Logical")
|
||||
.tag("Logical")
|
||||
Text("Finder")
|
||||
.tag("Finder")
|
||||
}
|
||||
.pickerStyle(themeSettings: themeSettings)
|
||||
|
||||
|
||||
}
|
||||
.padding(5)
|
||||
.padding(.leading)
|
||||
@@ -237,7 +195,7 @@ struct GeneralSettingsTab: View {
|
||||
.frame(width: 20, height: 20)
|
||||
.padding(.trailing)
|
||||
.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")
|
||||
.font(.callout)
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
@@ -265,7 +223,7 @@ struct GeneralSettingsTab: View {
|
||||
.frame(width: 20, height: 20)
|
||||
.padding(.trailing)
|
||||
.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")
|
||||
.font(.callout)
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
@@ -292,7 +250,7 @@ struct GeneralSettingsTab: View {
|
||||
.frame(width: 20, height: 20)
|
||||
.padding(.trailing)
|
||||
.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")
|
||||
.font(.callout)
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
@@ -330,7 +288,7 @@ struct GeneralSettingsTab: View {
|
||||
.frame(width: 20, height: 20)
|
||||
.padding(.trailing)
|
||||
.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")
|
||||
.font(.callout)
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
@@ -371,7 +329,7 @@ struct GeneralSettingsTab: View {
|
||||
.frame(width: 20, height: 20)
|
||||
.padding(.trailing)
|
||||
.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")
|
||||
.font(.callout)
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
|
||||
@@ -193,40 +193,15 @@ struct InterfaceSettingsTab: View {
|
||||
}
|
||||
InfoButton(text: "Changing the color mode will reset the base color to defaults")
|
||||
Spacer()
|
||||
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)
|
||||
}
|
||||
})
|
||||
Picker("", selection: $selectedTheme) {
|
||||
Text("Auto")
|
||||
.tag("Auto")
|
||||
Text("Dark")
|
||||
.tag("Dark")
|
||||
Text("Light")
|
||||
.tag("Light")
|
||||
}
|
||||
.pickerStyle(themeSettings: themeSettings)
|
||||
.onChange(of: selectedTheme) { newTheme in
|
||||
switch newTheme {
|
||||
case "Auto":
|
||||
|
||||
@@ -11,44 +11,36 @@ import Foundation
|
||||
|
||||
struct UpdateSettingsTab: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var themeSettings: ThemeSettings
|
||||
@State private var showAlert = false
|
||||
@State private var showDone = false
|
||||
@AppStorage("settings.updater.nextUpdateDate") private var nextUpdateDate = Date.now.timeIntervalSinceReferenceDate
|
||||
@AppStorage("settings.updater.updateTimeframe") private var updateTimeframe: Int = 1
|
||||
@AppStorage("settings.updater.enableUpdates") private var enableUpdates: Bool = true
|
||||
@AppStorage("settings.updater.updateFrequency") private var updateFrequency: UpdateFrequency = .daily
|
||||
|
||||
var body: some View {
|
||||
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 {
|
||||
HStack(spacing: 0) {
|
||||
Text("\(enableUpdates ? "Pearcleaner will check for updates every " : "Automatic updates are disabled")")
|
||||
|
||||
Text("Pearcleaner will check for updates")
|
||||
.font(.callout)
|
||||
.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()
|
||||
|
||||
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 {
|
||||
Text("Next update check: \(formattedDate(Date(timeIntervalSinceReferenceDate: nextUpdateDate)))")
|
||||
.font(.footnote)
|
||||
@@ -56,18 +48,9 @@ struct UpdateSettingsTab: View {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Spacer()
|
||||
Toggle(isOn: $enableUpdates, label: {
|
||||
})
|
||||
.toggleStyle(.switch)
|
||||
}
|
||||
.padding(5)
|
||||
.padding(.leading)
|
||||
.padding(5)
|
||||
.padding(.horizontal)
|
||||
|
||||
ScrollView {
|
||||
VStack() {
|
||||
@@ -83,8 +66,11 @@ struct UpdateSettingsTab: View {
|
||||
}
|
||||
.frame(minHeight: 0, maxHeight: .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")
|
||||
.font(.callout)
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
@@ -129,10 +115,6 @@ struct UpdateSettingsTab: View {
|
||||
}
|
||||
.padding(20)
|
||||
.frame(width: 500, height: 520)
|
||||
// .onAppear {
|
||||
// // Convert TimeInterval to Date on appearance
|
||||
// let _ = Date(timeIntervalSinceReferenceDate: nextUpdateDate)
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ struct AppListItems: View {
|
||||
}
|
||||
|
||||
if bundleSize == 0 {
|
||||
ProgressView().controlSize(.mini)
|
||||
ProgressView().controlSize(.mini).padding(.leading, 5)
|
||||
} else {
|
||||
Text("\(isHovered ? "v\(appInfo.appVersion)" : formatByte(size: bundleSize).human)")
|
||||
.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)
|
||||
// }
|
||||
// })
|
||||
|
||||
Reference in New Issue
Block a user