mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 03:48:55 +00:00
v3.6.1
This commit is contained in:
@@ -44,12 +44,13 @@
|
||||
C7DD49EA2BAB4D8100CCBA16 /* AppInfoFetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DD49E92BAB4D8100CCBA16 /* AppInfoFetch.swift */; };
|
||||
C7DD49EE2BAB7F6000CCBA16 /* ReversePathsFetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DD49ED2BAB7F6000CCBA16 /* ReversePathsFetch.swift */; };
|
||||
C7DD49F02BABA14400CCBA16 /* Folders.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DD49EF2BABA14400CCBA16 /* Folders.swift */; };
|
||||
C7DE17652BE54F4900197715 /* Updater.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DE17642BE54F4900197715 /* Updater.swift */; };
|
||||
C7DE672B2BA6343D00EB1633 /* MenuBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */; };
|
||||
C7ED86B12BA8A35500F13DE4 /* Interface.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ED86B02BA8A35500F13DE4 /* Interface.swift */; };
|
||||
C7ED86B32BA8ED8800F13DE4 /* Trash.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ED86B22BA8ED8800F13DE4 /* Trash.swift */; };
|
||||
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F539372AF60865007DF1B2 /* Utilities.swift */; };
|
||||
C7FB173B2B96321300B96F9A /* AppsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7FB173A2B96321300B96F9A /* AppsListView.swift */; };
|
||||
C7FEBA112BDC422200AE195F /* Searchbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7FEBA102BDC422200AE195F /* Searchbar.swift */; };
|
||||
C7FEBA112BDC422200AE195F /* AppSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7FEBA102BDC422200AE195F /* AppSearchView.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -138,12 +139,13 @@
|
||||
C7DD49E92BAB4D8100CCBA16 /* AppInfoFetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppInfoFetch.swift; sourceTree = "<group>"; };
|
||||
C7DD49ED2BAB7F6000CCBA16 /* ReversePathsFetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReversePathsFetch.swift; sourceTree = "<group>"; };
|
||||
C7DD49EF2BABA14400CCBA16 /* Folders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Folders.swift; sourceTree = "<group>"; };
|
||||
C7DE17642BE54F4900197715 /* Updater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Updater.swift; sourceTree = "<group>"; };
|
||||
C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarItem.swift; sourceTree = "<group>"; };
|
||||
C7ED86B02BA8A35500F13DE4 /* Interface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interface.swift; sourceTree = "<group>"; };
|
||||
C7ED86B22BA8ED8800F13DE4 /* Trash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Trash.swift; sourceTree = "<group>"; };
|
||||
C7F539372AF60865007DF1B2 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
|
||||
C7FB173A2B96321300B96F9A /* AppsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppsListView.swift; sourceTree = "<group>"; };
|
||||
C7FEBA102BDC422200AE195F /* Searchbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Searchbar.swift; sourceTree = "<group>"; };
|
||||
C7FEBA102BDC422200AE195F /* AppSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSearchView.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -202,6 +204,7 @@
|
||||
C7ACADE52B92A737000B5845 /* Features.swift */,
|
||||
C7ED86B22BA8ED8800F13DE4 /* Trash.swift */,
|
||||
C79D051F2BC99EB40083F976 /* ThemeManager.swift */,
|
||||
C7DE17642BE54F4900197715 /* Updater.swift */,
|
||||
);
|
||||
path = Logic;
|
||||
sourceTree = "<group>";
|
||||
@@ -299,7 +302,7 @@
|
||||
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */,
|
||||
C7045A272B03E71D00376976 /* MiniMode.swift */,
|
||||
C7FB173A2B96321300B96F9A /* AppsListView.swift */,
|
||||
C7FEBA102BDC422200AE195F /* Searchbar.swift */,
|
||||
C7FEBA102BDC422200AE195F /* AppSearchView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
@@ -458,6 +461,7 @@
|
||||
C7FB173B2B96321300B96F9A /* AppsListView.swift in Sources */,
|
||||
C7045A342B068B2900376976 /* UpdateView.swift in Sources */,
|
||||
C77B90162AF19377009CC655 /* AppState.swift in Sources */,
|
||||
C7DE17652BE54F4900197715 /* Updater.swift in Sources */,
|
||||
C7DD49EE2BAB7F6000CCBA16 /* ReversePathsFetch.swift in Sources */,
|
||||
C7045A322B068AD700376976 /* NewWin.swift in Sources */,
|
||||
C7DD49F02BABA14400CCBA16 /* Folders.swift in Sources */,
|
||||
@@ -485,7 +489,7 @@
|
||||
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */,
|
||||
C76D08552AF89CDE00D07867 /* RegularMode.swift in Sources */,
|
||||
C7A9CE472B89164700EB6E78 /* Authorization.swift in Sources */,
|
||||
C7FEBA112BDC422200AE195F /* Searchbar.swift in Sources */,
|
||||
C7FEBA112BDC422200AE195F /* AppSearchView.swift in Sources */,
|
||||
C7CF47242B3B3F1700979C5F /* WindowSettings.swift in Sources */,
|
||||
C77B90232AF2D616009CC655 /* FilesView.swift in Sources */,
|
||||
);
|
||||
@@ -556,8 +560,8 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
APP_BUILD = 39;
|
||||
APP_VERSION = 3.6.0;
|
||||
APP_BUILD = 40;
|
||||
APP_VERSION = 3.6.1;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
@@ -626,8 +630,8 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
APP_BUILD = 39;
|
||||
APP_VERSION = 3.6.0;
|
||||
APP_BUILD = 40;
|
||||
APP_VERSION = 3.6.1;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
|
||||
@@ -19,7 +19,6 @@ class AppState: ObservableObject
|
||||
@Published var zombieFile: ZombieFile
|
||||
@Published var sortedApps: [AppInfo] = []
|
||||
@Published var selectedItems = Set<URL>()
|
||||
// @Published var selectedZombieItems = Set<URL>()
|
||||
@Published var alertType = AlertType.off
|
||||
@Published var currentView = CurrentDetailsView.empty
|
||||
@Published var showAlert: Bool = false
|
||||
@@ -29,6 +28,7 @@ class AppState: ObservableObject
|
||||
@Published var reload: Bool = false
|
||||
@Published var showProgress: Bool = false
|
||||
@Published var finderExtensionEnabled: Bool = false
|
||||
@Published var updateAvailable: Bool = false
|
||||
|
||||
|
||||
init() {
|
||||
|
||||
@@ -17,8 +17,9 @@ struct SimpleButtonStyle: ButtonStyle {
|
||||
let color: Color
|
||||
let size: CGFloat
|
||||
let padding: CGFloat
|
||||
let rotate: Bool
|
||||
|
||||
init(icon: String, iconFlip: String = "", label: String = "", help: String, color: Color = Color("mode"), size: CGFloat = 20, padding: CGFloat = 5) {
|
||||
init(icon: String, iconFlip: String = "", label: String = "", help: String, color: Color = Color("mode"), size: CGFloat = 20, padding: CGFloat = 5, rotate: Bool = false) {
|
||||
self.icon = icon
|
||||
self.iconFlip = iconFlip
|
||||
self.label = label
|
||||
@@ -26,6 +27,7 @@ struct SimpleButtonStyle: ButtonStyle {
|
||||
self.color = color
|
||||
self.size = size
|
||||
self.padding = padding
|
||||
self.rotate = rotate
|
||||
}
|
||||
|
||||
func makeBody(configuration: Self.Configuration) -> some View {
|
||||
@@ -34,6 +36,9 @@ struct SimpleButtonStyle: ButtonStyle {
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: size, height: size)
|
||||
.scaleEffect(hovered ? 1.1 : 1.0)
|
||||
.rotationEffect(.degrees(rotate ? (hovered ? 90 : 0) : 0))
|
||||
.animation(.easeInOut(duration: 0.2), value: hovered)
|
||||
if !label.isEmpty {
|
||||
Text(label)
|
||||
}
|
||||
@@ -158,24 +163,33 @@ struct SimpleCheckboxToggleStyle: ToggleStyle {
|
||||
struct InfoButton: View {
|
||||
@State private var isPopoverPresented: Bool = false
|
||||
let text: String
|
||||
let color: Color?
|
||||
let label: String?
|
||||
let color: Color
|
||||
let label: String
|
||||
let warning: Bool
|
||||
|
||||
init(text: String, color: Color = Color("mode"), label: String = "", warning: Bool = false) {
|
||||
self.text = text
|
||||
self.color = color
|
||||
self.label = label
|
||||
self.warning = warning
|
||||
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Button(action: {
|
||||
self.isPopoverPresented.toggle()
|
||||
}) {
|
||||
HStack(alignment: .center, spacing: 5) {
|
||||
Image(systemName: "info.circle.fill")
|
||||
Image(systemName: !warning ? "info.circle.fill" : "exclamationmark.triangle.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 14, height: 14)
|
||||
.foregroundColor(color?.opacity(0.7) ?? Color("mode").opacity(0.7))
|
||||
.foregroundColor(!warning ? color.opacity(0.7) : color)
|
||||
.frame(height: 16)
|
||||
if !label!.isEmpty {
|
||||
Text(label!)
|
||||
if !label.isEmpty {
|
||||
Text(label)
|
||||
.font(.callout)
|
||||
.foregroundColor(color?.opacity(0.7) ?? Color("mode").opacity(0.7))
|
||||
.foregroundColor(color.opacity(0.7))
|
||||
|
||||
}
|
||||
}
|
||||
@@ -205,12 +219,15 @@ struct InfoButton: View {
|
||||
|
||||
|
||||
struct UninstallButton: ButtonStyle {
|
||||
@State private var hovered: Bool = false
|
||||
var isEnabled: Bool
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
HStack(alignment: .center, spacing: 10) {
|
||||
Image(systemName: "trash")
|
||||
Image(systemName: !hovered ? "trash" : "trash.fill")
|
||||
.foregroundColor(isEnabled ? .white.opacity(1) : .white.opacity(0.3))
|
||||
.scaleEffect(hovered ? 1.2 : 1.0)
|
||||
.animation(.easeInOut(duration: 0.2), value: hovered)
|
||||
|
||||
Divider()
|
||||
.frame(height: 24)
|
||||
@@ -227,11 +244,7 @@ struct UninstallButton: ButtonStyle {
|
||||
.background(configuration.isPressed ? Color("uninstall").opacity(0.8) : Color("uninstall"))
|
||||
.cornerRadius(8)
|
||||
.onHover { over in
|
||||
if over {
|
||||
NSCursor.pointingHand.push()
|
||||
} else {
|
||||
NSCursor.pop()
|
||||
}
|
||||
hovered = over
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,11 +252,14 @@ struct UninstallButton: ButtonStyle {
|
||||
|
||||
|
||||
struct RescanButton: ButtonStyle {
|
||||
@State private var hovered: Bool = false
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
HStack(alignment: .center, spacing: 10) {
|
||||
Image(systemName: "arrow.counterclockwise.circle")
|
||||
Image(systemName: !hovered ? "arrow.counterclockwise.circle" : "arrow.counterclockwise.circle.fill")
|
||||
.foregroundColor(.white)
|
||||
.scaleEffect(hovered ? 1.2 : 1.0)
|
||||
.animation(.easeInOut(duration: 0.2), value: hovered)
|
||||
|
||||
Divider()
|
||||
.frame(height: 24)
|
||||
@@ -261,11 +277,7 @@ struct RescanButton: ButtonStyle {
|
||||
.background(configuration.isPressed ? Color("button").opacity(0.8) : Color("button"))
|
||||
.cornerRadius(8)
|
||||
.onHover { over in
|
||||
if over {
|
||||
NSCursor.pointingHand.push()
|
||||
} else {
|
||||
NSCursor.pop()
|
||||
}
|
||||
hovered = over
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -606,12 +618,14 @@ struct WindowActionButton: ButtonStyle {
|
||||
struct SimpleButtonBrightStyle: ButtonStyle {
|
||||
@State private var hovered = false
|
||||
let icon: String
|
||||
let label: String
|
||||
let help: String
|
||||
let color: Color
|
||||
let shield: Bool?
|
||||
|
||||
init(icon: String, help: String, color: Color, shield: Bool? = nil) {
|
||||
init(icon: String, label: String = "", help: String, color: Color, shield: Bool? = nil) {
|
||||
self.icon = icon
|
||||
self.label = label
|
||||
self.help = help
|
||||
self.color = color
|
||||
self.shield = shield
|
||||
@@ -624,6 +638,7 @@ struct SimpleButtonBrightStyle: ButtonStyle {
|
||||
.scaledToFit()
|
||||
.frame(width: 20)
|
||||
.foregroundColor(hovered ? color.opacity(0.5) : color)
|
||||
Text(label)
|
||||
}
|
||||
.padding(5)
|
||||
.onHover { hovering in
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
//
|
||||
// Updater.swift
|
||||
// Pearcleaner
|
||||
//
|
||||
// Created by Alin Lupascu on 5/3/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
|
||||
struct Release: Codable {
|
||||
let id: Int
|
||||
let tag_name: String
|
||||
let body: String
|
||||
let assets: [Asset]
|
||||
}
|
||||
|
||||
struct Asset: Codable {
|
||||
let name: String
|
||||
let url: String
|
||||
let browser_download_url: String
|
||||
}
|
||||
|
||||
extension Release {
|
||||
var modifiedBody: String {
|
||||
return body.replacingOccurrences(of: "- [x]", with: ">").replacingOccurrences(of: "###", with: "")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
func loadGithubReleases(appState: AppState, manual: Bool = false) {
|
||||
let url = URL(string: "https://api.github.com/repos/alienator88/Pearcleaner/releases")!
|
||||
let request = URLRequest(url: url)
|
||||
URLSession.shared.dataTask(with: request) { data, response, error in
|
||||
if let data = data {
|
||||
if let decodedResponse = try? JSONDecoder().decode([Release].self, from: data) {
|
||||
DispatchQueue.main.async {
|
||||
let lastFewReleases = Array(decodedResponse.prefix(3)) // Get only the last 3 recent releases
|
||||
appState.releases = lastFewReleases
|
||||
checkForUpdate(appState: appState, manual: manual)
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
printOS("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
|
||||
}.resume()
|
||||
}
|
||||
|
||||
func checkForUpdate(appState: AppState, manual: Bool = false) {
|
||||
guard let latestRelease = appState.releases.first else { return }
|
||||
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
||||
if latestRelease.tag_name > currentVersion ?? "" {
|
||||
updateOnMain {
|
||||
appState.updateAvailable = true
|
||||
}
|
||||
} else {
|
||||
if manual {
|
||||
NewWin.show(appState: appState, width: 500, height: 300, newWin: .no_update)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func downloadUpdate(appState: AppState) {
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Getting update file links ready"
|
||||
appState.progressBar.1 = 0.1
|
||||
}
|
||||
|
||||
guard let latestRelease = appState.releases.first else { return }
|
||||
guard let asset = latestRelease.assets.first else { return }
|
||||
guard let url = URL(string: asset.url) else { return }
|
||||
var request = URLRequest(url: url)
|
||||
request.setValue("application/octet-stream", forHTTPHeaderField: "Accept")
|
||||
|
||||
let downloadTask = URLSession.shared.downloadTask(with: request) { localURL, urlResponse, error in
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Downloading update file"
|
||||
appState.progressBar.1 = 0.2
|
||||
}
|
||||
|
||||
guard let localURL = localURL else { return }
|
||||
|
||||
let fileManager = FileManager.default
|
||||
let destinationURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("Pearcleaner").appendingPathComponent("\(asset.name)")
|
||||
|
||||
do {
|
||||
if fileManager.fileExists(atPath: destinationURL.path) {
|
||||
try? fileManager.removeItem(at: destinationURL)
|
||||
}
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Moving update file to Application Support"
|
||||
appState.progressBar.1 = 0.4
|
||||
}
|
||||
|
||||
try fileManager.moveItem(at: localURL, to: destinationURL)
|
||||
|
||||
UnzipAndReplace(DownloadedFileURL: destinationURL.path, appState: appState)
|
||||
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Done, please restart!"
|
||||
appState.progressBar.1 = 1.0
|
||||
appState.updateAvailable = false
|
||||
}
|
||||
|
||||
|
||||
} catch {
|
||||
printOS("Error moving downloaded file: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
downloadTask.resume()
|
||||
}
|
||||
|
||||
func UnzipAndReplace(DownloadedFileURL fileURL: String, appState: AppState) {
|
||||
let appDirectory = Bundle.main.bundleURL.deletingLastPathComponent().path
|
||||
let appBundle = Bundle.main.bundleURL.path
|
||||
let fileManager = FileManager.default
|
||||
|
||||
do {
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Deleting existing application"
|
||||
appState.progressBar.1 = 0.5
|
||||
}
|
||||
|
||||
// Remove the old version of your app
|
||||
try fileManager.removeItem(atPath: appBundle)
|
||||
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Unziping new update file to original Pearcleaner location"
|
||||
appState.progressBar.1 = 0.6
|
||||
}
|
||||
|
||||
|
||||
// Unzip the downloaded update file to your app's bundle path
|
||||
let process = Process()
|
||||
process.executableURL = URL(fileURLWithPath: "/usr/bin/ditto")
|
||||
process.arguments = ["-xk", fileURL, appDirectory]
|
||||
process.standardOutput = FileHandle.nullDevice
|
||||
process.standardError = FileHandle.nullDevice
|
||||
|
||||
try process.run()
|
||||
process.waitUntilExit()
|
||||
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Deleting update file"
|
||||
appState.progressBar.1 = 0.8
|
||||
}
|
||||
|
||||
// After unzipping, remove the update file
|
||||
try fileManager.removeItem(atPath: fileURL)
|
||||
|
||||
|
||||
} catch {
|
||||
printOS("Error updating the app: \(error)")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct UpdateNotificationView: View {
|
||||
let appState: AppState
|
||||
@State private var hovered: Bool = false
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
|
||||
Text("Update Available")
|
||||
.font(.callout)
|
||||
.opacity(0.5)
|
||||
.padding(.leading, 7)
|
||||
|
||||
Spacer()
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Image(systemName: !hovered ? "square.and.arrow.down" : "square.and.arrow.down.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 14, height: 14)
|
||||
.animation(.easeInOut(duration: 0.2), value: hovered)
|
||||
.foregroundStyle(.white)
|
||||
|
||||
|
||||
Text("v\(appState.releases.first?.tag_name ?? "Update")")
|
||||
.foregroundStyle(.white)
|
||||
|
||||
}
|
||||
.padding(3)
|
||||
.onHover { hovering in
|
||||
withAnimation() {
|
||||
hovered = hovering
|
||||
}
|
||||
}
|
||||
.help("Download latest update")
|
||||
.padding(.horizontal, 5)
|
||||
.padding(.vertical, 4)
|
||||
.background(Color("pear"))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 6))
|
||||
.onTapGesture {
|
||||
NewWin.show(appState: appState, width: 500, height: 440, newWin: .update)
|
||||
}
|
||||
}
|
||||
.frame(height: 30)
|
||||
.padding(5)
|
||||
.background(Color("mode").opacity(0.05))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 6))
|
||||
.padding(.horizontal)
|
||||
.padding(.bottom)
|
||||
}
|
||||
}
|
||||
@@ -27,22 +27,15 @@ struct FolderSettingsTab: View {
|
||||
VStack(spacing: 0) {
|
||||
|
||||
HStack(spacing: 0) {
|
||||
Text("Apps").font(.title2)
|
||||
InfoButton(text: "Locations that will be searched for .app files. Click a non-default path to remove it. Add new folders using the + button or drag/drop over the list. Non-default paths can't be removed.", color: nil, label: "")
|
||||
Text("Application Folders").font(.title2)
|
||||
InfoButton(text: "Locations that will be searched for .app files. Click a non-default path to remove it. Add new folders using the + button or drag/drop over the list. Non-default paths can't be removed.")
|
||||
.padding(.leading, 5)
|
||||
Spacer()
|
||||
|
||||
Button("") {
|
||||
selectFolder()
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "plus", help: "Add folder"))
|
||||
.onHover(perform: { hovering in
|
||||
if hovering {
|
||||
NSCursor.pointingHand.push()
|
||||
|
||||
} else {
|
||||
NSCursor.pop()
|
||||
}
|
||||
})
|
||||
.buttonStyle(SimpleButtonStyle(icon: "plus.circle", help: "Add folder", size: 16, rotate: true))
|
||||
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
@@ -55,6 +48,8 @@ struct FolderSettingsTab: View {
|
||||
|
||||
Text(fsm.folderPaths[index])
|
||||
.font(.callout)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
.opacity(fsm.defaultPaths.contains(fsm.folderPaths[index]) ? 0.5 : 1)
|
||||
.padding(5)
|
||||
Spacer()
|
||||
@@ -105,6 +100,13 @@ struct FolderSettingsTab: View {
|
||||
return true
|
||||
}
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
Text("Drop folders here").opacity(0.5)
|
||||
Spacer()
|
||||
}
|
||||
.padding(.top, 5)
|
||||
|
||||
|
||||
Divider()
|
||||
.padding(.vertical)
|
||||
@@ -112,21 +114,15 @@ struct FolderSettingsTab: View {
|
||||
// === LEFTOVER FILES ================================================================================================
|
||||
|
||||
HStack(spacing: 0) {
|
||||
Text("Leftover Files").font(.title2)
|
||||
InfoButton(text: "Add files or folders that will be ignored when searching for leftover files. Click a path to remove it from the list. Add new files/folders using the + button or drag/drop over the list.", color: nil, label: "")
|
||||
Text("Leftover Files Exclusions").font(.title2)
|
||||
InfoButton(text: "Add files or folders that will be ignored when searching for leftover files. Click a path to remove it from the list. Add new files/folders using the + button or drag/drop over the list.")
|
||||
.padding(.leading, 5)
|
||||
Spacer()
|
||||
|
||||
Button("") {
|
||||
selectFilesFoldersZ()
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "plus", help: "Add file/folder"))
|
||||
.onHover(perform: { hovering in
|
||||
if hovering {
|
||||
NSCursor.pointingHand.push()
|
||||
} else {
|
||||
NSCursor.pop()
|
||||
}
|
||||
})
|
||||
.buttonStyle(SimpleButtonStyle(icon: "plus.circle", help: "Add file/folder", size: 16, rotate: true))
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
|
||||
@@ -148,6 +144,8 @@ struct FolderSettingsTab: View {
|
||||
|
||||
Text(fsm.fileFolderPathsZ[index])
|
||||
.font(.callout)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
.padding(5)
|
||||
Spacer()
|
||||
}
|
||||
@@ -193,7 +191,12 @@ struct FolderSettingsTab: View {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
Text("Drop files/folders here").opacity(0.5)
|
||||
Spacer()
|
||||
}
|
||||
.padding(.top, 5)
|
||||
|
||||
|
||||
Spacer()
|
||||
|
||||
@@ -56,7 +56,7 @@ struct GeneralSettingsTab: View {
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
}
|
||||
|
||||
InfoButton(text: "When homebrew cleanup is enabled, Pearcleaner will check if the app you are removing was installed via homebrew and execute a brew uninstall and brew cleanup command as well to let homebrew know that the app is removed. This way your homebrew list will be synced up correctly and caching will be removed.\n\nNOTE: If you undo the file delete with CMD+Z, the files will be put back but homebrew will not be aware of it. To get the homebrew list back in sync you'd need to run:\n brew install APPNAME --force", color: nil, label: "")
|
||||
InfoButton(text: "When homebrew cleanup is enabled, Pearcleaner will check if the app you are removing was installed via homebrew and execute a brew uninstall and brew cleanup command as well to let homebrew know that the app is removed. This way your homebrew list will be synced up correctly and caching will be removed.\n\nNOTE: If you undo the file delete with CMD+Z, the files will be put back but homebrew will not be aware of it. To get the homebrew list back in sync you'd need to run:\n brew install APPNAME --force")
|
||||
|
||||
Spacer()
|
||||
Toggle(isOn: $brew, label: {
|
||||
@@ -79,7 +79,7 @@ struct GeneralSettingsTab: View {
|
||||
.font(.callout)
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
}
|
||||
InfoButton(text: "When searching for app files or leftover files, the list will be sorted either alphabetically or by size(large to small)", color: nil, label: "")
|
||||
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"],
|
||||
@@ -123,7 +123,7 @@ struct GeneralSettingsTab: View {
|
||||
.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.", color: nil, label: "")
|
||||
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"],
|
||||
@@ -294,7 +294,7 @@ struct GeneralSettingsTab: View {
|
||||
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))
|
||||
InfoButton(text: "Enabling the extension will allow you to right click apps in Finder and quickly uninstall them with Pearcleaner", color: nil, label: "")
|
||||
InfoButton(text: "Enabling the extension will allow you to right click apps in Finder and quickly uninstall them with Pearcleaner")
|
||||
|
||||
Spacer()
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ struct InterfaceSettingsTab: View {
|
||||
.foregroundStyle(Color("mode").opacity(0.3))
|
||||
.padding(.leading, 5)
|
||||
}
|
||||
// InfoButton(text: "The logo animation is only available in macOS 14 or higher", color: nil, label: "")
|
||||
// InfoButton(text: "The logo animation is only available in macOS 14 or higher")
|
||||
Spacer()
|
||||
Toggle(isOn: $animateLogo, label: {
|
||||
})
|
||||
@@ -120,7 +120,7 @@ struct InterfaceSettingsTab: View {
|
||||
.font(.callout)
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
}
|
||||
InfoButton(text: "When using a custom color, you might need to change the application color mode below to Dark or Light so text is readable", color: nil, label: "")
|
||||
InfoButton(text: "When using a custom color, you might need to change the application color mode below to Dark or Light so text is readable")
|
||||
Spacer()
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ struct InterfaceSettingsTab: View {
|
||||
.font(.callout)
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
}
|
||||
InfoButton(text: "Changing the color mode will reset the base color to defaults", color: nil, label: "")
|
||||
InfoButton(text: "Changing the color mode will reset the base color to defaults")
|
||||
Spacer()
|
||||
SegmentedPicker(
|
||||
["Auto", "Dark", "Light"],
|
||||
@@ -358,7 +358,7 @@ struct InterfaceSettingsTab: View {
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
}
|
||||
|
||||
InfoButton(text: "In mini window mode, you can have Pearcleaner startup to the Apps List view or the Drop Target view.", color: nil, label: "")
|
||||
InfoButton(text: "In mini window mode, you can have Pearcleaner startup to the Apps List view or the Drop Target view.")
|
||||
|
||||
Spacer()
|
||||
Toggle(isOn: $miniView, label: {
|
||||
@@ -385,7 +385,7 @@ struct InterfaceSettingsTab: View {
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
}
|
||||
|
||||
InfoButton(text: "In mini window mode, if you pin the Files popover on top, clicking away from the window will not dismiss it. Otherwise, it will dismiss by clicking anywhere outside the popover.", color: nil, label: "")
|
||||
InfoButton(text: "In mini window mode, if you pin the Files popover on top, clicking away from the window will not dismiss it. Otherwise, it will dismiss by clicking anywhere outside the popover.")
|
||||
|
||||
Spacer()
|
||||
Toggle(isOn: $popoverStay, label: {
|
||||
@@ -420,7 +420,7 @@ struct InterfaceSettingsTab: View {
|
||||
.font(.callout)
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
}
|
||||
InfoButton(text: "When menubar icon is enabled, the main app window and dock icon will be disabled since the app will be put in accessory mode.", color: nil, label: "")
|
||||
InfoButton(text: "When menubar icon is enabled, the main app window and dock icon will be disabled since the app will be put in accessory mode.")
|
||||
Spacer()
|
||||
Toggle(isOn: $menubarEnabled, label: {
|
||||
})
|
||||
@@ -485,7 +485,7 @@ struct InterfaceSettingsTab: View {
|
||||
.font(.callout)
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
}
|
||||
InfoButton(text: "This setting will affect Pearcleaner whether you're running in menubar mode or regular mode. If you disable menubar icon, you might want to disable this as well so Pearcleaner doesn't start on login.", color: nil, label: "")
|
||||
InfoButton(text: "This setting will affect Pearcleaner whether you're running in menubar mode or regular mode. If you disable menubar icon, you might want to disable this as well so Pearcleaner doesn't start on login.")
|
||||
Spacer()
|
||||
Toggle(isOn: $isLaunchAtLoginEnabled, label: {
|
||||
})
|
||||
|
||||
@@ -9,26 +9,6 @@
|
||||
import SwiftUI
|
||||
import Foundation
|
||||
|
||||
struct Release: Codable {
|
||||
let id: Int
|
||||
let tag_name: String
|
||||
let body: String
|
||||
let assets: [Asset]
|
||||
}
|
||||
|
||||
struct Asset: Codable {
|
||||
let name: String
|
||||
let url: String
|
||||
let browser_download_url: String
|
||||
}
|
||||
|
||||
extension Release {
|
||||
var modifiedBody: String {
|
||||
return body.replacingOccurrences(of: "- [x]", with: ">").replacingOccurrences(of: "###", with: "")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct UpdateSettingsTab: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
@State private var showAlert = false
|
||||
@@ -111,135 +91,3 @@ struct UpdateSettingsTab: View {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
func loadGithubReleases(appState: AppState, manual: Bool = false) {
|
||||
let url = URL(string: "https://api.github.com/repos/alienator88/Pearcleaner/releases")!
|
||||
let request = URLRequest(url: url)
|
||||
// Set the token for a private repo
|
||||
// request.setValue("token \(ghToken)", forHTTPHeaderField: "Authorization")
|
||||
URLSession.shared.dataTask(with: request) { data, response, error in
|
||||
if let data = data {
|
||||
if let decodedResponse = try? JSONDecoder().decode([Release].self, from: data) {
|
||||
DispatchQueue.main.async {
|
||||
let lastFewReleases = Array(decodedResponse.prefix(3)) // Get only the last 3 recent releases
|
||||
appState.releases = lastFewReleases
|
||||
|
||||
checkForUpdate(appState: appState, manual: manual)
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
printOS("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
|
||||
}.resume()
|
||||
}
|
||||
|
||||
func checkForUpdate(appState: AppState, manual: Bool = false) {
|
||||
guard let latestRelease = appState.releases.first else { return }
|
||||
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
||||
if latestRelease.tag_name > currentVersion ?? "" {
|
||||
NewWin.show(appState: appState, width: 500, height: 440, newWin: .update)
|
||||
} else {
|
||||
if manual {
|
||||
NewWin.show(appState: appState, width: 500, height: 300, newWin: .no_update)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func downloadUpdate(appState: AppState) {
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Getting update file links ready"
|
||||
appState.progressBar.1 = 0.1
|
||||
}
|
||||
|
||||
guard let latestRelease = appState.releases.first else { return }
|
||||
guard let asset = latestRelease.assets.first else { return }
|
||||
guard let url = URL(string: asset.url) else { return }
|
||||
var request = URLRequest(url: url)
|
||||
// request.setValue("token \(ghToken)", forHTTPHeaderField: "Authorization")
|
||||
request.setValue("application/octet-stream", forHTTPHeaderField: "Accept")
|
||||
|
||||
let downloadTask = URLSession.shared.downloadTask(with: request) { localURL, urlResponse, error in
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Downloading update file"
|
||||
appState.progressBar.1 = 0.2
|
||||
}
|
||||
|
||||
guard let localURL = localURL else { return }
|
||||
|
||||
let fileManager = FileManager.default
|
||||
let destinationURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("Pearcleaner").appendingPathComponent("\(asset.name)")
|
||||
|
||||
do {
|
||||
if fileManager.fileExists(atPath: destinationURL.path) {
|
||||
try? fileManager.removeItem(at: destinationURL)
|
||||
}
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Moving update file to Application Support"
|
||||
appState.progressBar.1 = 0.4
|
||||
}
|
||||
|
||||
try fileManager.moveItem(at: localURL, to: destinationURL)
|
||||
|
||||
UnzipAndReplace(DownloadedFileURL: destinationURL.path, appState: appState)
|
||||
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Done, please restart!"
|
||||
appState.progressBar.1 = 1.0
|
||||
}
|
||||
|
||||
|
||||
} catch {
|
||||
printOS("Error moving downloaded file: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
downloadTask.resume()
|
||||
}
|
||||
|
||||
func UnzipAndReplace(DownloadedFileURL fileURL: String, appState: AppState) {
|
||||
let appDirectory = Bundle.main.bundleURL.deletingLastPathComponent().path
|
||||
let appBundle = Bundle.main.bundleURL.path
|
||||
let fileManager = FileManager.default
|
||||
|
||||
do {
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Deleting existing application"
|
||||
appState.progressBar.1 = 0.5
|
||||
}
|
||||
|
||||
// Remove the old version of your app
|
||||
try fileManager.removeItem(atPath: appBundle)
|
||||
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Unziping new update file to original Pearcleaner location"
|
||||
appState.progressBar.1 = 0.6
|
||||
}
|
||||
|
||||
|
||||
// Unzip the downloaded update file to your app's bundle path
|
||||
let process = Process()
|
||||
process.executableURL = URL(fileURLWithPath: "/usr/bin/ditto")
|
||||
process.arguments = ["-xk", fileURL, appDirectory]
|
||||
process.standardOutput = FileHandle.nullDevice
|
||||
process.standardError = FileHandle.nullDevice
|
||||
|
||||
try process.run()
|
||||
process.waitUntilExit()
|
||||
|
||||
updateOnMain {
|
||||
appState.progressBar.0 = "Deleting update file"
|
||||
appState.progressBar.1 = 0.8
|
||||
}
|
||||
|
||||
// After unzipping, remove the update file
|
||||
try fileManager.removeItem(atPath: fileURL)
|
||||
|
||||
|
||||
} catch {
|
||||
printOS("Error updating the app: \(error)")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct Searchbar: View {
|
||||
struct AppSearchView: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var themeSettings: ThemeSettings
|
||||
@EnvironmentObject var locations: Locations
|
||||
@@ -26,9 +26,13 @@ struct Searchbar: View {
|
||||
VStack(alignment: .center, spacing: 0) {
|
||||
Spacer()
|
||||
.frame(height: 10)
|
||||
.padding(.top, !menubarEnabled ? 25 : 0)
|
||||
|
||||
if appState.updateAvailable {
|
||||
UpdateNotificationView(appState: appState)
|
||||
}
|
||||
|
||||
AppsListView(search: $search, showPopover: $showPopover, filteredApps: filteredApps)
|
||||
.padding(.top, 25)
|
||||
|
||||
Divider()
|
||||
|
||||
@@ -42,7 +46,7 @@ struct Searchbar: View {
|
||||
Button("More") {
|
||||
self.showMenu.toggle()
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "ellipsis.circle", help: "More"))
|
||||
.buttonStyle(SimpleButtonStyle(icon: "ellipsis.circle", help: "More", rotate: true))
|
||||
.popover(isPresented: $showMenu) {
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
@@ -122,7 +122,7 @@ struct FilesView: View {
|
||||
Text("•").foregroundStyle(Color("AccentColor"))
|
||||
Text("\(appState.appInfo.appVersion)").font(.title3)
|
||||
if appState.appInfo.appName.count < 5 {
|
||||
InfoButton(text: "Pearcleaner searches for files via a combination of bundle id and app name. \(appState.appInfo.appName) has a common or short app name so there might be unrelated files found. Please check the list thoroughly before uninstalling.", color: nil, label: "")
|
||||
InfoButton(text: "Pearcleaner searches for files via a combination of bundle id and app name. \(appState.appInfo.appName) has a common or short app name so there might be unrelated files found. Please check the list thoroughly before uninstalling.")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -392,7 +392,7 @@ struct FileDetailsItem: View {
|
||||
}
|
||||
|
||||
if isNested(path: path) {
|
||||
InfoButton(text: "Application file is nested within subdirectories. To prevent deleting incorrect folders, Pearcleaner will leave these alone. You may manually delete the remaining folders if required.", color: nil, label: "")
|
||||
InfoButton(text: "Application file is nested within subdirectories. To prevent deleting incorrect folders, Pearcleaner will leave these alone. You may manually delete the remaining folders if required.")
|
||||
}
|
||||
|
||||
if let imageView = folderImages(for: path.path) {
|
||||
|
||||
@@ -166,7 +166,7 @@ struct MiniAppView: View {
|
||||
}
|
||||
.padding(.vertical)
|
||||
} else {
|
||||
Searchbar(glass: glass, sidebarWidth: sidebarWidth, menubarEnabled: menubarEnabled, mini: mini, search: $search, showPopover: $showPopover)
|
||||
AppSearchView(glass: glass, sidebarWidth: sidebarWidth, menubarEnabled: menubarEnabled, mini: mini, search: $search, showPopover: $showPopover)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ struct RegularMode: View {
|
||||
.frame(width: sidebarWidth)
|
||||
.padding(.vertical)
|
||||
} else {
|
||||
Searchbar(glass: glass, sidebarWidth: sidebarWidth, menubarEnabled: menubarEnabled, mini: mini, search: $search, showPopover: $showPopover)
|
||||
AppSearchView(glass: glass, sidebarWidth: sidebarWidth, menubarEnabled: menubarEnabled, mini: mini, search: $search, showPopover: $showPopover)
|
||||
.frame(width: sidebarWidth)
|
||||
}
|
||||
|
||||
|
||||
@@ -150,11 +150,11 @@ struct ZombieView: View {
|
||||
VStack(alignment: .leading, spacing: 10){
|
||||
HStack {
|
||||
Text("Leftover Files").font(.title).fontWeight(.bold)
|
||||
InfoButton(text: "Leftover file search is not 100% accurate as it doesn't have any app bundles to check against. This searches for files/folders and excludes the ones that have overlap with your currently installed apps. Make sure to confirm files marked for removal are correct.", color: .red, label: "READ")
|
||||
InfoButton(text: "Leftover file search is not and can never be 100% accurate as it doesn't have any old/uninstalled app bundles to check against for file exclusion. This does a best guess search for files/folders and excludes the ones that have overlap with your currently installed applications. Please confirm files marked for deletion really do belong to old/uninstalled applications.", color: .orange, warning: true)
|
||||
Spacer()
|
||||
|
||||
}
|
||||
Text("Remaining files and folders from previous installs")
|
||||
Text("Remaining files and folders from previous applications")
|
||||
.font(.callout).foregroundStyle(Color("mode").opacity(0.5))
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ struct PermView: View {
|
||||
}) {
|
||||
Text("Restart")
|
||||
}
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.uturn.left.circle", help: "Restart", color: .red))
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.uturn.left.circle", label: "Restart", help: "Restart", color: .red))
|
||||
.padding()
|
||||
|
||||
Button(action: {
|
||||
@@ -87,7 +87,7 @@ struct PermView: View {
|
||||
}) {
|
||||
Text("Full Disk Access")
|
||||
}
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "externaldrive", help: "Full Disk", color: .accentColor))
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "externaldrive", label: "Full Disk", help: "Full Disk", color: .accentColor))
|
||||
|
||||
.padding()
|
||||
|
||||
@@ -98,7 +98,7 @@ struct PermView: View {
|
||||
}) {
|
||||
Text("Accessibility")
|
||||
}
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "accessibility", help: "Accessibility", color: .accentColor))
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "accessibility", label: "Accessibility", help: "Accessibility", color: .accentColor))
|
||||
.padding()
|
||||
|
||||
|
||||
|
||||
@@ -73,15 +73,15 @@ struct UpdateView: View {
|
||||
Button(action: NewWin.close) {
|
||||
Text("Cancel")
|
||||
}
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "x.circle", help: "Close", color: .red))
|
||||
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "x.circle", label: "Close", help: "Close", color: .red))
|
||||
|
||||
if appState.progressBar.1 != 1.0 {
|
||||
Button(action: {
|
||||
downloadUpdate(appState: appState)
|
||||
}) {
|
||||
Text("Update")
|
||||
}
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.down.circle", help: "Update", color: .accentColor))
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.down.circle", label: "Update", help: "Update", color: .accentColor))
|
||||
} else {
|
||||
Button(action: {
|
||||
NewWin.close()
|
||||
@@ -89,7 +89,7 @@ struct UpdateView: View {
|
||||
}) {
|
||||
Text("Restart")
|
||||
}
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.uturn.left.circle", help: "Restart", color: .accentColor))
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.uturn.left.circle", label: "Restart", help: "Restart", color: .accentColor))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -178,14 +178,14 @@ struct NoUpdateView: View {
|
||||
}) {
|
||||
Text("Force Update")
|
||||
}
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.down.circle", help: "Force-Update", color: .red))
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.down.circle", label: "Force Update", help: "Force-Update", color: .red))
|
||||
|
||||
Button(action: {
|
||||
NewWin.close()
|
||||
}) {
|
||||
Text("Okay")
|
||||
}
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "checkmark.circle", help: "Ok", color: .accentColor))
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "checkmark.circle", label: "Ok", help: "Ok", color: .accentColor))
|
||||
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ struct NoUpdateView: View {
|
||||
}) {
|
||||
Text("Restart")
|
||||
}
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.uturn.left.circle", help: "Restart", color: .accentColor))
|
||||
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.uturn.left.circle", label: "Restart", help: "Restart", color: .accentColor))
|
||||
}
|
||||
}
|
||||
.padding(.bottom)
|
||||
|
||||
Reference in New Issue
Block a user