mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 07:19:04 +00:00
v3.7.3
This commit is contained in:
@@ -48,6 +48,7 @@
|
|||||||
C7DD49F02BABA14400CCBA16 /* Folders.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DD49EF2BABA14400CCBA16 /* Folders.swift */; };
|
C7DD49F02BABA14400CCBA16 /* Folders.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DD49EF2BABA14400CCBA16 /* Folders.swift */; };
|
||||||
C7DE17652BE54F4900197715 /* Updater.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DE17642BE54F4900197715 /* Updater.swift */; };
|
C7DE17652BE54F4900197715 /* Updater.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DE17642BE54F4900197715 /* Updater.swift */; };
|
||||||
C7DE672B2BA6343D00EB1633 /* MenuBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */; };
|
C7DE672B2BA6343D00EB1633 /* MenuBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */; };
|
||||||
|
C7EA06332C1CBE2000F872FC /* ConditionBuilderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7EA06322C1CBE2000F872FC /* ConditionBuilderView.swift */; };
|
||||||
C7ED86B12BA8A35500F13DE4 /* Interface.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ED86B02BA8A35500F13DE4 /* Interface.swift */; };
|
C7ED86B12BA8A35500F13DE4 /* Interface.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ED86B02BA8A35500F13DE4 /* Interface.swift */; };
|
||||||
C7ED86B32BA8ED8800F13DE4 /* Trash.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ED86B22BA8ED8800F13DE4 /* Trash.swift */; };
|
C7ED86B32BA8ED8800F13DE4 /* Trash.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ED86B22BA8ED8800F13DE4 /* Trash.swift */; };
|
||||||
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F539372AF60865007DF1B2 /* Utilities.swift */; };
|
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F539372AF60865007DF1B2 /* Utilities.swift */; };
|
||||||
@@ -145,6 +146,7 @@
|
|||||||
C7DD49EF2BABA14400CCBA16 /* Folders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Folders.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>"; };
|
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>"; };
|
C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarItem.swift; sourceTree = "<group>"; };
|
||||||
|
C7EA06322C1CBE2000F872FC /* ConditionBuilderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionBuilderView.swift; sourceTree = "<group>"; };
|
||||||
C7ED86B02BA8A35500F13DE4 /* Interface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interface.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>"; };
|
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>"; };
|
C7F539372AF60865007DF1B2 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
|
||||||
@@ -309,6 +311,7 @@
|
|||||||
C7FB173A2B96321300B96F9A /* AppsListView.swift */,
|
C7FB173A2B96321300B96F9A /* AppsListView.swift */,
|
||||||
C7FEBA102BDC422200AE195F /* AppSearchView.swift */,
|
C7FEBA102BDC422200AE195F /* AppSearchView.swift */,
|
||||||
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */,
|
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */,
|
||||||
|
C7EA06322C1CBE2000F872FC /* ConditionBuilderView.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -471,6 +474,7 @@
|
|||||||
C7DE17652BE54F4900197715 /* Updater.swift in Sources */,
|
C7DE17652BE54F4900197715 /* Updater.swift in Sources */,
|
||||||
C7DD49EE2BAB7F6000CCBA16 /* ReversePathsFetch.swift in Sources */,
|
C7DD49EE2BAB7F6000CCBA16 /* ReversePathsFetch.swift in Sources */,
|
||||||
C7045A322B068AD700376976 /* NewWin.swift in Sources */,
|
C7045A322B068AD700376976 /* NewWin.swift in Sources */,
|
||||||
|
C7EA06332C1CBE2000F872FC /* ConditionBuilderView.swift in Sources */,
|
||||||
C7DD49F02BABA14400CCBA16 /* Folders.swift in Sources */,
|
C7DD49F02BABA14400CCBA16 /* Folders.swift in Sources */,
|
||||||
C7ED86B32BA8ED8800F13DE4 /* Trash.swift in Sources */,
|
C7ED86B32BA8ED8800F13DE4 /* Trash.swift in Sources */,
|
||||||
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */,
|
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */,
|
||||||
@@ -568,8 +572,8 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
APP_BUILD = 47;
|
APP_BUILD = 48;
|
||||||
APP_VERSION = 3.7.2;
|
APP_VERSION = 3.7.3;
|
||||||
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 +642,8 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
APP_BUILD = 47;
|
APP_BUILD = 48;
|
||||||
APP_VERSION = 3.7.2;
|
APP_VERSION = 3.7.3;
|
||||||
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;
|
||||||
|
|||||||
@@ -153,17 +153,15 @@ class AppPathFinder {
|
|||||||
|
|
||||||
for condition in conditions {
|
for condition in conditions {
|
||||||
if bundleIdentifierL.contains(condition.bundle_id) {
|
if bundleIdentifierL.contains(condition.bundle_id) {
|
||||||
// Exclude and include keywords
|
// Exclude keywords
|
||||||
let hasIncludeKeyword = condition.include.contains(where: itemL.contains)
|
|
||||||
let hasExcludeKeyword = condition.exclude.contains(where: itemL.contains)
|
let hasExcludeKeyword = condition.exclude.contains(where: itemL.contains)
|
||||||
|
|
||||||
if hasExcludeKeyword {
|
if hasExcludeKeyword {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
// Include keywords
|
||||||
|
let hasIncludeKeyword = condition.include.contains(where: itemL.contains)
|
||||||
if hasIncludeKeyword {
|
if hasIncludeKeyword {
|
||||||
if !condition.exclude.contains(where: itemL.contains) {
|
return true
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,7 +228,7 @@ class AppPathFinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private func handleOutliers() -> [URL] {
|
private func handleOutliers(include: Bool = true) -> [URL] {
|
||||||
var outliers: [URL] = []
|
var outliers: [URL] = []
|
||||||
let bundleIdentifier = self.appInfo.bundleIdentifier.pearFormat()
|
let bundleIdentifier = self.appInfo.bundleIdentifier.pearFormat()
|
||||||
|
|
||||||
@@ -239,14 +237,24 @@ class AppPathFinder {
|
|||||||
bundleIdentifier.contains(condition.bundle_id)
|
bundleIdentifier.contains(condition.bundle_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for condition in matchingConditions {
|
for condition in matchingConditions {
|
||||||
if let forceIncludes = condition.includeForce {
|
if include {
|
||||||
for path in forceIncludes {
|
// Handle includeForce
|
||||||
if let url = URL(string: path), FileManager.default.fileExists(atPath: url.path) {
|
if let forceIncludes = condition.includeForce {
|
||||||
outliers.append(url)
|
for path in forceIncludes {
|
||||||
|
outliers.append(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle excludeForce
|
||||||
|
if let excludeForce = condition.excludeForce {
|
||||||
|
for path in excludeForce {
|
||||||
|
outliers.append(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return outliers
|
return outliers
|
||||||
@@ -256,6 +264,7 @@ class AppPathFinder {
|
|||||||
DispatchQueue.global(qos: .userInitiated).async {
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
let allContainers = self.getAllContainers(bundleURL: self.appInfo.path)
|
let allContainers = self.getAllContainers(bundleURL: self.appInfo.path)
|
||||||
let outliers = self.handleOutliers()
|
let outliers = self.handleOutliers()
|
||||||
|
let outliersEx = self.handleOutliers(include: false)
|
||||||
var tempCollection: [URL] = []
|
var tempCollection: [URL] = []
|
||||||
self.collectionAccessQueue.sync {
|
self.collectionAccessQueue.sync {
|
||||||
tempCollection = self.collection
|
tempCollection = self.collection
|
||||||
@@ -263,6 +272,12 @@ class AppPathFinder {
|
|||||||
tempCollection.append(contentsOf: allContainers)
|
tempCollection.append(contentsOf: allContainers)
|
||||||
tempCollection.append(contentsOf: outliers)
|
tempCollection.append(contentsOf: outliers)
|
||||||
|
|
||||||
|
// Remove URLs based on outliersExcludes
|
||||||
|
let excludePaths = outliersEx.map { $0.path }
|
||||||
|
tempCollection.removeAll { url in
|
||||||
|
excludePaths.contains(url.path)
|
||||||
|
}
|
||||||
|
|
||||||
// Sort and standardize URLs to ensure consistent comparisons
|
// Sort and standardize URLs to ensure consistent comparisons
|
||||||
let sortedCollection = tempCollection.map { $0.standardizedFileURL }.sorted(by: { $0.path < $1.path })
|
let sortedCollection = tempCollection.map { $0.standardizedFileURL }.sorted(by: { $0.path < $1.path })
|
||||||
var filteredCollection: [URL] = []
|
var filteredCollection: [URL] = []
|
||||||
|
|||||||
@@ -6,13 +6,32 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import SwiftData
|
||||||
|
|
||||||
|
struct Condition: Codable {
|
||||||
struct Condition: Decodable {
|
|
||||||
var bundle_id: String
|
var bundle_id: String
|
||||||
var include: [String]
|
var include: [String]
|
||||||
var exclude: [String]
|
var exclude: [String]
|
||||||
var includeForce: [String]?
|
var includeForce: [URL]?
|
||||||
|
var excludeForce: [URL]?
|
||||||
|
|
||||||
|
init(bundle_id: String, include: [String], exclude: [String], includeForce: [String]? = nil, excludeForce: [String]? = nil) {
|
||||||
|
self.bundle_id = bundle_id.pearFormat()
|
||||||
|
self.include = include.map { $0.pearFormat() }
|
||||||
|
self.exclude = exclude.map { $0.pearFormat() }
|
||||||
|
self.includeForce = includeForce?.compactMap { path in
|
||||||
|
if let url = URL(string: path), FileManager.default.fileExists(atPath: url.path) {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
self.excludeForce = excludeForce?.compactMap { path in
|
||||||
|
if let url = URL(string: path), FileManager.default.fileExists(atPath: url.path) {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SkipCondition {
|
struct SkipCondition {
|
||||||
@@ -25,70 +44,75 @@ struct SkipCondition {
|
|||||||
// Conditions for some apps that need to include/exclude certain files/folders when names are more complicated
|
// Conditions for some apps that need to include/exclude certain files/folders when names are more complicated
|
||||||
var conditions: [Condition] = [
|
var conditions: [Condition] = [
|
||||||
Condition(
|
Condition(
|
||||||
bundle_id: "comappledtxcode",
|
bundle_id: "com.apple.dt.xcode",
|
||||||
include: ["comappledt", "xcode", "simulator"],
|
include: ["com.apple.dt", "xcode", "simulator"],
|
||||||
exclude: ["comrobotsandpencilsxcodesapp", "comoneminutegamesxcodecleaner", "iohyperappxcodecleaner", "xcodesjson"],
|
exclude: ["com.robotsandpencils.xcodesapp", "com.oneminutegames.xcodecleaner", "io.hyperapp.xcodecleaner", "xcodes.json"],
|
||||||
includeForce: ["\(home)/Library/Containers/com.apple.iphonesimulator.ShareExtension"]
|
includeForce: ["\(home)/Library/Containers/com.apple.iphonesimulator.ShareExtension"]
|
||||||
),
|
),
|
||||||
Condition(
|
Condition(
|
||||||
bundle_id: "comrobotsandpencilsxcodesapp",
|
bundle_id: "com.robotsandpencils.xcodesapp",
|
||||||
include: [],
|
include: [],
|
||||||
exclude: ["comappledtxcode", "comoneminutegamesxcodecleaner", "iohyperappxcodecleaner"]
|
exclude: ["com.apple.dt.xcode", "com.oneminutegames.xcodecleaner", "io.hyperapp.xcodecleaner"]
|
||||||
),
|
),
|
||||||
Condition(
|
Condition(
|
||||||
bundle_id: "iohyperappxcodecleaner",
|
bundle_id: "io.hyperapp.xcodecleaner",
|
||||||
include: [],
|
include: [],
|
||||||
exclude: ["comrobotsandpencilsxcodesapp", "comoneminutegamesxcodecleaner", "comappledtxcode", "xcodesjson"]
|
exclude: ["com.robotsandpencils.xcodesapp", "com.oneminutegames.xcodecleaner", "com.apple.dt.xcode", "xcodes.json"]
|
||||||
),
|
),
|
||||||
Condition(
|
Condition(
|
||||||
bundle_id: "uszoomxos",
|
bundle_id: "us.zoom.xos",
|
||||||
include: ["zoom"],
|
include: ["zoom"],
|
||||||
exclude: []
|
exclude: []
|
||||||
),
|
),
|
||||||
Condition(
|
Condition(
|
||||||
bundle_id: "combravebrowser",
|
bundle_id: "com.brave.browser",
|
||||||
include: ["brave"],
|
include: ["brave"],
|
||||||
exclude: []
|
exclude: []
|
||||||
),
|
),
|
||||||
Condition(
|
Condition(
|
||||||
bundle_id: "comoktamobile",
|
bundle_id: "com.okta.mobile",
|
||||||
include: ["okta"],
|
include: ["okta"],
|
||||||
exclude: []
|
exclude: []
|
||||||
),
|
),
|
||||||
Condition(
|
Condition(
|
||||||
bundle_id: "comgooglechrome",
|
bundle_id: "com.google.chrome",
|
||||||
include: ["google", "chrome"],
|
include: ["google", "chrome"],
|
||||||
exclude: ["iterm", "chromefeaturestate"]
|
exclude: ["iterm", "chromefeaturestate"]
|
||||||
),
|
),
|
||||||
Condition(
|
Condition(
|
||||||
bundle_id: "commicrosoftedgemac",
|
bundle_id: "com.microsoft.edgemac",
|
||||||
include: ["microsoft"],
|
include: ["microsoft"],
|
||||||
exclude: ["vscode", "rdc", "appcenter", "office", "oneauth"]
|
exclude: ["vscode", "rdc", "appcenter", "office", "oneauth"]
|
||||||
),
|
),
|
||||||
Condition(
|
Condition(
|
||||||
bundle_id: "orgmozillafirefox",
|
bundle_id: "org.mozilla.firefox",
|
||||||
include: ["mozilla", "firefox"],
|
include: ["mozilla", "firefox"],
|
||||||
exclude: []
|
exclude: []
|
||||||
),
|
),
|
||||||
Condition(
|
Condition(
|
||||||
bundle_id: "comlogioptionsplus",
|
bundle_id: "org.mozilla.firefox.nightly",
|
||||||
|
include: ["mozilla", "firefox"],
|
||||||
|
exclude: []
|
||||||
|
),
|
||||||
|
Condition(
|
||||||
|
bundle_id: "com.logi.optionsplus",
|
||||||
include: ["logi"],
|
include: ["logi"],
|
||||||
exclude: ["login", "logic"],
|
exclude: ["login", "logic"],
|
||||||
includeForce: []
|
includeForce: []
|
||||||
),
|
),
|
||||||
Condition(
|
Condition(
|
||||||
bundle_id: "commicrosoftvscode",
|
bundle_id: "com.microsoft.vscode",
|
||||||
include: ["vscode"],
|
include: ["vscode"],
|
||||||
exclude: [],
|
exclude: [],
|
||||||
includeForce: ["\(home)/Library/Application Support/Code"]
|
includeForce: ["\(home)/Library/Application Support/Code/"]
|
||||||
),
|
),
|
||||||
Condition(
|
Condition(
|
||||||
bundle_id: "comfacebookarchondeveloperid",
|
bundle_id: "com.facebook.archon.developerid",
|
||||||
include: ["archonloginhelper"],
|
include: ["archon.loginhelper"],
|
||||||
exclude: []
|
exclude: []
|
||||||
),
|
),
|
||||||
Condition(
|
Condition(
|
||||||
bundle_id: "euexelbanstats",
|
bundle_id: "eu.exelban.stats",
|
||||||
include: [],
|
include: [],
|
||||||
exclude: ["video"]
|
exclude: ["video"]
|
||||||
),
|
),
|
||||||
@@ -114,36 +138,59 @@ let skipConditions: [SkipCondition] = [
|
|||||||
let skipReverse = ["apple", "temporary", "btserver", "proapps", "scripteditor", "ilife", "livefsd", "siritoday", "addressbook", "animoji", "appstore", "askpermission", "callhistory", "clouddocs", "diskimages", "dock", "facetime", "fileprovider", "instruments", "knowledge", "mobilesync", "syncservices", "homeenergyd", "icloud", "icdd", "networkserviceproxy", "familycircle", "geoservices", "installation", "passkit", "sharedimagecache", "desktop", "mbuseragent", "swiftpm", "baseband", "coresimulator", "photoslegacyupgrade", "photosupgrade", "siritts", "ipod", "globalpreferences", "apmanalytics", "apmexperiment", "avatarcache", "byhost", "contextstoreagent", "mobilemeaccounts", "intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd", "clang", "audiocomponent", "csexattrcryptoservice", "livetranscriptionagent", "sandboxhelper", "statuskitagent", "betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed", "heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd", "metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd", "talagent", "watchlistd", "apptranslocation", "xcrun", "ds_store", "caches", "crashreporter", "trash", "pearcleaner", "amsdatamigratortool", "arfilecache", "assistant", "chromium", "cloudkit", "webkit", "databases", "diagnostic", "cache", "gamekit", "homebrew", "logi", "microsoft", "mozilla", "sync", "google", "sentinel", "hexnode", "sentry", "tvappservices", "reminders"]
|
let skipReverse = ["apple", "temporary", "btserver", "proapps", "scripteditor", "ilife", "livefsd", "siritoday", "addressbook", "animoji", "appstore", "askpermission", "callhistory", "clouddocs", "diskimages", "dock", "facetime", "fileprovider", "instruments", "knowledge", "mobilesync", "syncservices", "homeenergyd", "icloud", "icdd", "networkserviceproxy", "familycircle", "geoservices", "installation", "passkit", "sharedimagecache", "desktop", "mbuseragent", "swiftpm", "baseband", "coresimulator", "photoslegacyupgrade", "photosupgrade", "siritts", "ipod", "globalpreferences", "apmanalytics", "apmexperiment", "avatarcache", "byhost", "contextstoreagent", "mobilemeaccounts", "intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd", "clang", "audiocomponent", "csexattrcryptoservice", "livetranscriptionagent", "sandboxhelper", "statuskitagent", "betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed", "heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd", "metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd", "talagent", "watchlistd", "apptranslocation", "xcrun", "ds_store", "caches", "crashreporter", "trash", "pearcleaner", "amsdatamigratortool", "arfilecache", "assistant", "chromium", "cloudkit", "webkit", "databases", "diagnostic", "cache", "gamekit", "homebrew", "logi", "microsoft", "mozilla", "sync", "google", "sentinel", "hexnode", "sentry", "tvappservices", "reminders"]
|
||||||
|
|
||||||
|
|
||||||
// Function to load additional conditions from a GitHub JSON file
|
|
||||||
func loadConditionsFromGitHub() {
|
|
||||||
let url = URL(string: "https://api.github.com/repos/alienator88/Pearcleaner/contents/conditions.json")!
|
|
||||||
var request = URLRequest(url: url)
|
|
||||||
request.setValue("application/vnd.github.VERSION.raw", forHTTPHeaderField: "Accept")
|
|
||||||
request.setValue("2022-11-28", forHTTPHeaderField: "X-GitHub-Api-Version")
|
|
||||||
|
|
||||||
URLSession.shared.dataTask(with: request) { data, response, error in
|
|
||||||
if let data = data {
|
|
||||||
do {
|
|
||||||
// Assuming the JSON structure directly maps to an array of Condition
|
|
||||||
let jsonObject = try JSONSerialization.jsonObject(with: data, options: [])
|
|
||||||
if let conditionArray = jsonObject as? [[String: Any]] {
|
|
||||||
let jsonData = try JSONSerialization.data(withJSONObject: conditionArray, options: [])
|
|
||||||
let additionalConditions = try JSONDecoder().decode([Condition].self, from: jsonData)
|
|
||||||
|
|
||||||
if !additionalConditions.isEmpty {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
conditions += additionalConditions
|
// Store and load conditions locally via SwiftData
|
||||||
}
|
class ConditionManager {
|
||||||
}
|
static let shared = ConditionManager()
|
||||||
} else {
|
|
||||||
printOS("The data format is incorrect or empty for GitHub conditions processing.")
|
private init() {
|
||||||
}
|
loadConditions()
|
||||||
} catch {
|
}
|
||||||
printOS("Failed to decode conditions JSON: \(error.localizedDescription)")
|
|
||||||
}
|
// Function to save a condition
|
||||||
} else {
|
func saveCondition(_ condition: Condition) {
|
||||||
printOS("Failed to fetch conditions data: \(error?.localizedDescription ?? "Unknown error")")
|
if condition.include.isEmpty && condition.exclude.isEmpty && (condition.includeForce?.isEmpty ?? true) {
|
||||||
|
deleteCondition(bundle_id: condition.bundle_id)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}.resume()
|
let defaults = UserDefaults.standard
|
||||||
}
|
let encoder = JSONEncoder()
|
||||||
|
let key = "Condition-\(condition.bundle_id)"
|
||||||
|
|
||||||
|
if let encoded = try? encoder.encode(condition) {
|
||||||
|
defaults.set(encoded, forKey: key)
|
||||||
|
conditions.append(condition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to delete a condition from defaults and conditions variable
|
||||||
|
func deleteCondition(bundle_id: String) {
|
||||||
|
let defaults = UserDefaults.standard
|
||||||
|
let key = "Condition-\(bundle_id.pearFormat())"
|
||||||
|
|
||||||
|
// Remove from UserDefaults
|
||||||
|
defaults.removeObject(forKey: key)
|
||||||
|
|
||||||
|
// Remove from conditions variable
|
||||||
|
conditions.removeAll { $0.bundle_id == bundle_id.pearFormat() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to load a condition and append to the global variable
|
||||||
|
func loadConditions() {
|
||||||
|
let defaults = UserDefaults.standard
|
||||||
|
let decoder = JSONDecoder()
|
||||||
|
|
||||||
|
for (key, value) in defaults.dictionaryRepresentation() {
|
||||||
|
if key.starts(with: "Condition-"), let savedCondition = value as? Data {
|
||||||
|
if let loadedCondition = try? decoder.decode(Condition.self, from: savedCondition) {
|
||||||
|
conditions.append(loadedCondition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class Locations: ObservableObject {
|
|||||||
self.tempDir = tempDir
|
self.tempDir = tempDir
|
||||||
|
|
||||||
self.apps = Category(name: "Apps", paths: [
|
self.apps = Category(name: "Apps", paths: [
|
||||||
|
"\(home)",
|
||||||
"\(home)/Library",
|
"\(home)/Library",
|
||||||
"\(home)/Library/Application Scripts",
|
"\(home)/Library/Application Scripts",
|
||||||
"\(home)/Library/Application Support",
|
"\(home)/Library/Application Support",
|
||||||
|
|||||||
@@ -630,6 +630,20 @@ struct AnimatedSearchStyle: TextFieldStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct RoundedTextFieldStyle: TextFieldStyle {
|
||||||
|
func _body(configuration: TextField<Self._Label>) -> some View {
|
||||||
|
configuration
|
||||||
|
.padding(8)
|
||||||
|
.cornerRadius(6)
|
||||||
|
.textFieldStyle(.plain)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.strokeBorder(Color("mode").opacity(0.4), lineWidth: 0.8)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct GlassEffect: NSViewRepresentable {
|
struct GlassEffect: NSViewRepresentable {
|
||||||
var material: NSVisualEffectView.Material // Choose the material you want
|
var material: NSVisualEffectView.Material // Choose the material you want
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ func loadGithubReleases(appState: AppState, manual: Bool = false, releaseOnly: B
|
|||||||
}.resume()
|
}.resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkForUpdate(appState: AppState, manual: Bool = false) {
|
func checkForUpdate(appState: AppState, manual: Bool) {
|
||||||
guard let latestRelease = appState.releases.first else { return }
|
guard let latestRelease = appState.releases.first else { return }
|
||||||
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0"
|
||||||
if latestRelease.tag_name > currentVersion ?? "" {
|
if latestRelease.tag_name > currentVersion {
|
||||||
updateOnMain {
|
updateOnMain {
|
||||||
appState.updateAvailable = true
|
appState.updateAvailable = true
|
||||||
}
|
}
|
||||||
@@ -198,14 +198,6 @@ enum UpdateFrequency: String, CaseIterable, Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//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.updateFrequency") var updateFrequency: UpdateFrequency = .daily
|
@AppStorage("settings.updater.updateFrequency") var updateFrequency: UpdateFrequency = .daily
|
||||||
@@ -230,7 +222,7 @@ func checkAndUpdateIfNeeded(appState: AppState) {
|
|||||||
|
|
||||||
func updateApp(appState: AppState) {
|
func updateApp(appState: AppState) {
|
||||||
// Perform your update logic here
|
// Perform your update logic here
|
||||||
printOS("Updater: performing update")
|
printOS("Updater: checking for update")
|
||||||
loadGithubReleases(appState: appState)
|
loadGithubReleases(appState: appState)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,54 +235,6 @@ 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
|
||||||
|
|
||||||
|
|||||||
@@ -427,6 +427,31 @@ extension String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Returns comma separated string as array of strings
|
||||||
|
extension String {
|
||||||
|
func toConditionFormat() -> [String] {
|
||||||
|
if self.isEmpty {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return self.components(separatedBy: ",").map { $0.trimmingCharacters(in: .whitespaces) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Overload the greater than operator ">" to do a semantic check on the string versions
|
||||||
|
extension String {
|
||||||
|
func versionStringToTuple() -> (Int, Int, Int) {
|
||||||
|
let components = self.split(separator: ".").compactMap { Int($0) }
|
||||||
|
return (components[0], components[1], components[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
static func > (lhs: String, rhs: String) -> Bool {
|
||||||
|
let lhsVersion = lhs.versionStringToTuple()
|
||||||
|
let rhsVersion = rhs.versionStringToTuple()
|
||||||
|
return lhsVersion > rhsVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- Trash Relationship ---
|
// --- Trash Relationship ---
|
||||||
//extension FileManager {
|
//extension FileManager {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ struct PearcleanerApp: App {
|
|||||||
@AppStorage("settings.interface.selectedMenubarIcon") var selectedMenubarIcon: String = "trash"
|
@AppStorage("settings.interface.selectedMenubarIcon") var selectedMenubarIcon: String = "trash"
|
||||||
@State private var search = ""
|
@State private var search = ""
|
||||||
@State private var showPopover: Bool = false
|
@State private var showPopover: Bool = false
|
||||||
|
let conditionManager = ConditionManager.shared
|
||||||
|
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
@@ -127,7 +127,7 @@ struct PearcleanerApp: App {
|
|||||||
// Get new features
|
// Get new features
|
||||||
getFeatures(appState: appState, features: $features)
|
getFeatures(appState: appState, features: $features)
|
||||||
// Load extra conditions from GitHub
|
// Load extra conditions from GitHub
|
||||||
loadConditionsFromGitHub()
|
// loadConditionsFromGitHub() // REPLACE THIS WITH NEW CONDITION MANAGER
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ struct SimpleSearchStyle: TextFieldStyle {
|
|||||||
extension NSTextView {
|
extension NSTextView {
|
||||||
open override var frame: CGRect {
|
open override var frame: CGRect {
|
||||||
didSet {
|
didSet {
|
||||||
insertionPointColor = .clear
|
insertionPointColor = NSColor(Color("mode").opacity(0.2))//.clear
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
//
|
||||||
|
// ConditionBuilderView.swift
|
||||||
|
// Pearcleaner
|
||||||
|
//
|
||||||
|
// Created by Alin Lupascu on 6/14/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ConditionBuilderView: View {
|
||||||
|
@Binding var showAlert:Bool
|
||||||
|
@State private var include = ""
|
||||||
|
@State private var exclude = ""
|
||||||
|
@State private var paths = ""
|
||||||
|
@State private var pathsEx = ""
|
||||||
|
@State var bundle: String
|
||||||
|
@State private var conditionExists = false
|
||||||
|
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 10) {
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
Text("Condition Builder")
|
||||||
|
.font(.headline)
|
||||||
|
Spacer()
|
||||||
|
Button("Close") {
|
||||||
|
showAlert = false
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "x.circle", iconFlip: "x.circle.fill", help: "Close"))
|
||||||
|
}
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
Spacer()
|
||||||
|
InfoButton(text: "Some files/folders are not similar to the app name or bundle id, causing Pearcleaner to either not find them or find unrelated files. \nTo combat this, you may create a custom condition for each application bundle using keywords or direct paths. \n\n- Can add file/folder keywords that you want to either include or exclude in fuzzy searches. \n\n- Can explicitly add or remove a full Finder path to search results if you want a direct search.", label: "Instructions", edge: .bottom)
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
Text("Include Keywords:").font(.callout)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
HStack {
|
||||||
|
TextField("keyword-1, keyword-2", text: $include)
|
||||||
|
.textFieldStyle(RoundedTextFieldStyle())
|
||||||
|
Button("Clear") {
|
||||||
|
include = ""
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "xmark.circle.fill", help: "Clear", size: 15))
|
||||||
|
}
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
Text("Exclude Keywords:").font(.callout)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
HStack {
|
||||||
|
TextField("keyword-1, keyword-2", text: $exclude)
|
||||||
|
.textFieldStyle(RoundedTextFieldStyle())
|
||||||
|
Button("Clear") {
|
||||||
|
exclude = ""
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "xmark.circle.fill", help: "Clear", size: 15))
|
||||||
|
}
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
Text("Add Direct Paths:").font(.callout)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
HStack {
|
||||||
|
TextField("/Full/Path/example-1.txt, /Full/Path/example-2.txt", text: $paths)
|
||||||
|
.textFieldStyle(RoundedTextFieldStyle())
|
||||||
|
Button("Clear") {
|
||||||
|
paths = ""
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "xmark.circle.fill", help: "Clear", size: 15))
|
||||||
|
}
|
||||||
|
HStack {
|
||||||
|
Text("Remove Direct Paths:").font(.callout)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
HStack {
|
||||||
|
TextField("/Full/Path/example-1.txt, /Full/Path/example-2.txt", text: $pathsEx)
|
||||||
|
.textFieldStyle(RoundedTextFieldStyle())
|
||||||
|
Button("Clear") {
|
||||||
|
pathsEx = ""
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "xmark.circle.fill", help: "Clear", size: 15))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Button("Add/Save") {
|
||||||
|
showAlert = false
|
||||||
|
let newCondition = Condition(bundle_id: bundle, include: include.toConditionFormat(), exclude: exclude.toConditionFormat(), includeForce: paths.toConditionFormat(), excludeForce: pathsEx.toConditionFormat())
|
||||||
|
ConditionManager.shared.saveCondition(newCondition)
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "plus.square.fill", label: conditionExists ? "Save" : "Add", help: "Save the condition for this application"))
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Button("Remove") {
|
||||||
|
showAlert = false
|
||||||
|
include = ""
|
||||||
|
exclude = ""
|
||||||
|
paths = ""
|
||||||
|
pathsEx = ""
|
||||||
|
ConditionManager.shared.deleteCondition(bundle_id: bundle)
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "minus.square.fill", label: "Remove", help: "Remove the condition from this application"))
|
||||||
|
.disabled(!conditionExists)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(15)
|
||||||
|
.frame(width: 500, height: 500)
|
||||||
|
.background(GlassEffect(material: .hudWindow, blendingMode: .behindWindow))
|
||||||
|
.onAppear {
|
||||||
|
loadCondition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadCondition() {
|
||||||
|
let defaults = UserDefaults.standard
|
||||||
|
let decoder = JSONDecoder()
|
||||||
|
|
||||||
|
let key = "Condition-\(bundle.pearFormat())"
|
||||||
|
if let savedCondition = defaults.object(forKey: key) as? Data {
|
||||||
|
if let loadedCondition = try? decoder.decode(Condition.self, from: savedCondition) {
|
||||||
|
include = loadedCondition.include.joined(separator: ", ")
|
||||||
|
exclude = loadedCondition.exclude.joined(separator: ", ")
|
||||||
|
if let includeForce = loadedCondition.includeForce {
|
||||||
|
paths = includeForce.map { $0.absoluteString }.joined(separator: ", ")
|
||||||
|
}
|
||||||
|
if let excludeForce = loadedCondition.excludeForce {
|
||||||
|
pathsEx = excludeForce.map { $0.absoluteString }.joined(separator: ", ")
|
||||||
|
}
|
||||||
|
conditionExists = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -20,6 +20,7 @@ struct FilesView: View {
|
|||||||
@AppStorage("settings.general.sizeType") var sizeType: String = "Real"
|
@AppStorage("settings.general.sizeType") var sizeType: String = "Real"
|
||||||
@AppStorage("settings.general.filesWarning") private var warning: Bool = false
|
@AppStorage("settings.general.filesWarning") private var warning: Bool = false
|
||||||
@State private var showAlert = false
|
@State private var showAlert = false
|
||||||
|
@State private var showConditionBuilder = false
|
||||||
@Environment(\.colorScheme) var colorScheme
|
@Environment(\.colorScheme) var colorScheme
|
||||||
@Binding var showPopover: Bool
|
@Binding var showPopover: Bool
|
||||||
@Binding var search: String
|
@Binding var search: String
|
||||||
@@ -277,6 +278,14 @@ struct FilesView: View {
|
|||||||
.padding(.top)
|
.padding(.top)
|
||||||
|
|
||||||
|
|
||||||
|
Button("Builder") {
|
||||||
|
showConditionBuilder = true
|
||||||
|
}
|
||||||
|
.buttonStyle(SimpleButtonStyle(icon: "hammer.fill", help: "Condition Builder", size: 14))
|
||||||
|
.padding(.top)
|
||||||
|
.padding(.trailing, 5)
|
||||||
|
|
||||||
|
|
||||||
Button("\(sizeType == "Logical" ? totalSelectedSize.logical : sizeType == "Finder" ? totalSelectedSize.finder : totalSelectedSize.real)") {
|
Button("\(sizeType == "Logical" ? totalSelectedSize.logical : sizeType == "Finder" ? totalSelectedSize.finder : totalSelectedSize.real)") {
|
||||||
Task {
|
Task {
|
||||||
|
|
||||||
@@ -389,6 +398,9 @@ struct FilesView: View {
|
|||||||
.frame(width: 400, height: 200)
|
.frame(width: 400, height: 200)
|
||||||
.background(GlassEffect(material: .hudWindow, blendingMode: .behindWindow))
|
.background(GlassEffect(material: .hudWindow, blendingMode: .behindWindow))
|
||||||
})
|
})
|
||||||
|
.sheet(isPresented: $showConditionBuilder, content: {
|
||||||
|
ConditionBuilderView(showAlert: $showConditionBuilder, bundle: appState.appInfo.bundleIdentifier)
|
||||||
|
})
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if !warning {
|
if !warning {
|
||||||
showAlert = true
|
showAlert = true
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"bundle_id": "orgmozillanightly",
|
|
||||||
"include": ["mozilla", "firefox"],
|
|
||||||
"exclude": [],
|
|
||||||
"includeForce": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
Reference in New Issue
Block a user