+ Start of app exclusion

This commit is contained in:
David Bureš 2025-10-26 19:38:49 +01:00
parent 5fe9447ca1
commit 2ef0b76354
No known key found for this signature in database
4 changed files with 101 additions and 1 deletions

View File

@ -92,6 +92,9 @@ struct CorkApp: App
.modelContainer(for: [
SavedTaggedPackage.self
])
.modelContainer(for: [
BrewPackagesTracker.ExcludedAdoptableApp.self
])
.task
{
NSWindow.allowsAutomaticWindowTabbing = false

View File

@ -6612,6 +6612,18 @@
}
}
},
"action.package-adoption.ignore.%@" : {
"comment" : "A button that allows a user to ignore a particular app that is being recommended for adoption. The argument is the name of the app.",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Ignore %@"
}
}
}
},
"action.preview-package-app-would-be-adopted-as.%@" : {
"comment" : "A context menu item that previews the app that would be adopted. The argument is a localized string describing the app that would be adopted.",
"isCommentAutoGenerated" : true,

View File

@ -7,6 +7,7 @@
import CorkShared
import Foundation
import SwiftData
extension BrewPackagesTracker
{
@ -156,8 +157,67 @@ extension BrewPackagesTracker
{
return try? .init(from: self.fullAppUrl)
}
func excludeSelf() async
{
let excludedAppRepresentation: BrewPackagesTracker.ExcludedAdoptableApp = .init(fromAdoptableApp: self)
await excludedAppRepresentation.saveSelfToDatabase()
}
func includeSelf() async
{
let excludedAppRepresentation: BrewPackagesTracker.ExcludedAdoptableApp = .init(fromAdoptableApp: self)
await excludedAppRepresentation.deleteSelfFromDatabase()
}
}
@Model
final class ExcludedAdoptableApp
{
@Attribute(.unique) @Attribute(.spotlight)
var appExecutable: String
init(appExecutable: String)
{
self.appExecutable = appExecutable
}
init(fromAdoptableApp app: BrewPackagesTracker.AdoptableApp)
{
self.appExecutable = app.appExecutable
}
@MainActor
public func saveSelfToDatabase()
{
AppConstants.shared.modelContainer.mainContext.insert(self)
}
@MainActor
public func deleteSelfFromDatabase()
{
let modelContext: ModelContext = AppConstants.shared.modelContainer.mainContext
do
{
let descriptor = FetchDescriptor<ExcludedAdoptableApp>(
predicate: #Predicate { $0.appExecutable == appExecutable }
)
if let existingPackage = try modelContext.fetch(descriptor).first
{
modelContext.delete(existingPackage)
}
}
catch
{
AppConstants.shared.logger.error("Failed to fetch excluded adoptable app for deletion: \(error.localizedDescription)")
}
}
}
/// Take the array that contains all Casks, and transform them into a list of Cask names, associated with their executable names for comparing with the contents of the Applications folder
private nonisolated
func processParsedAvailableCasks(

View File

@ -8,6 +8,8 @@
import CorkShared
import Defaults
import SwiftUI
import ButtonKit
import SwiftData
struct AdoptablePackagesSection: View
{
@ -17,6 +19,8 @@ struct AdoptablePackagesSection: View
@Environment(BrewPackagesTracker.self) var brewPackagesTracker: BrewPackagesTracker
@State private var isShowingAdoptionWarning: Bool = false
@Query private var excludedApps: [BrewPackagesTracker.ExcludedAdoptableApp]
var body: some View
{
@ -66,7 +70,11 @@ struct AdoptablePackagesSection: View
{
isShowingAdoptionWarning = false
appState.showSheet(ofType: .massAppAdoption(appsToAdopt: brewPackagesTracker.adoptableAppsSelectedToBeAdopted))
appState.showSheet(ofType:
.massAppAdoption(
appsToAdopt: brewPackagesTracker.adoptableAppsSelectedToBeAdopted
)
)
} label: {
Text("action.adopt-packages.longer")
}
@ -255,6 +263,23 @@ struct AdoptablePackageListItem: View
} label: {
Label("action.reveal-\(adoptableCask.appExecutable)-in-finder", systemImage: "finder")
}
Divider()
ignoreAdoptableAppButton(appToIgnore: adoptableCask)
}
}
@ViewBuilder
func ignoreAdoptableAppButton(appToIgnore: BrewPackagesTracker.AdoptableApp) -> some View
{
AsyncButton
{
AppConstants.shared.logger.info("Adding app \(appToIgnore.appExecutable) to the excluded apps")
await appToIgnore.excludeSelf()
} label: {
Label("action.package-adoption.ignore.\(appToIgnore.appExecutable)", systemImage: "xmark.circle")
}
}
}