Skip to content

Commit 5c8fb48

Browse files
committed
Homebrew category section fixes
1 parent 36815de commit 5c8fb48

File tree

2 files changed

+85
-65
lines changed

2 files changed

+85
-65
lines changed

Builds/changes.md

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
### What's New
22

3-
- [x] Add SUDO_ASKPASS prompt for homebrew auto update script to prompt user for password for privileged app updates
4-
- [x] Add leaves toggle in Formula category header on Installed tab (shows only formulae you installed directly, no dependencies)
5-
- [x] Use homebrew API jws file for Installed/Available packages information gathering
6-
- [x] Move FDA permission check outside of AF package to local scope to avoid race condition
7-
- [x] In Updater tab > Sidebar, added toggle to show/hide auto_updates apps. If app exists in Homebrew and Sparkle, only show Sparkle version unless auto_updates bool is on.
8-
- [x] Add debug lines to the log when updating a sparkle app in case it fails for some reason
9-
- [x] Show unsupported apps category in updater view, can hide it using Sidebar settings - #406
10-
- [x] Disable auto-slimming setting temporarily, causing some hangs on app exit - #405
3+
- [x] Flush bundle cache after homebrew cask update
4+
- [x] Load JWS API data on Homebrew view appear
5+
- [x] To cut down on complexity, formulae from Updater view as it will be for serving GUI apps only. Homebrew view for CLI packages.
6+
- [x] Add smart thread chunking for app update checking based on CPU
117

128

139
### Fixes
1410

15-
- [x] Fix cask icon reloading in Installed view
16-
- [x] Fix homebrew issues mentioned in pinned homebrew issue, hopefully..probably not 😪
17-
- [x] Fix Brew Auto Update toggle enabling - #401
11+
- [x] Fix homebrew underscore versions only cleaned locally, missed the API version which caused showing updates as 1.2.3 > 1.2.3 incorrectly
12+
- [x] Fix unsupported category toggle shouldn’t refresh apps list
13+
- [x] Fix leaf logic to show actual installed on request packages only
14+
- [x] Hidden updates section duplicates on refresh - #381
15+
- [x] Fix showing PWA apps in unsupported section
16+
- [x] Slim pearcleaner crashing app on close, disable feature for now - #405
1817
- [x] Translations

Pearcleaner/Views/Brew/SearchInstallSection.swift

Lines changed: 75 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ struct SearchInstallSection: View {
2121
@Environment(\.colorScheme) var colorScheme
2222
@State private var searchQuery: String = ""
2323
@State private var searchType: HomebrewSearchType = .installed
24-
@State private var collapsedCategories: Set<String> = []
24+
@State private var installedCollapsedCategories: Set<String> = []
25+
@State private var availableCollapsedCategories: Set<String> = []
2526
@State private var updatingPackages: Set<String> = []
2627
@State private var isUpdatingAll: Bool = false
2728
@AppStorage("settings.interface.scrollIndicators") private var scrollIndicators: Bool = false
@@ -155,11 +156,19 @@ struct SearchInstallSection: View {
155156
}
156157

157158

158-
private func toggleCategoryCollapse(for category: String) {
159-
if collapsedCategories.contains(category) {
160-
collapsedCategories.remove(category)
159+
private func toggleCategoryCollapse(for category: String, tab: HomebrewSearchType) {
160+
if tab == .installed {
161+
if installedCollapsedCategories.contains(category) {
162+
installedCollapsedCategories.remove(category)
163+
} else {
164+
installedCollapsedCategories.insert(category)
165+
}
161166
} else {
162-
collapsedCategories.insert(category)
167+
if availableCollapsedCategories.contains(category) {
168+
availableCollapsedCategories.remove(category)
169+
} else {
170+
availableCollapsedCategories.insert(category)
171+
}
163172
}
164173
}
165174

@@ -276,27 +285,29 @@ struct SearchInstallSection: View {
276285
let outdatedPackages = brewManager.installedByCategory[.outdated] ?? []
277286
let filteredOutdated = searchQuery.isEmpty ? outdatedPackages : outdatedPackages.filter { matchesSearchQuery($0, query: searchQuery) }
278287

279-
InstalledCategoryView(
280-
category: .outdated,
281-
packages: filteredOutdated,
282-
isLoading: brewManager.isLoadingOutdated,
283-
collapsed: filteredOutdated.isEmpty || collapsedCategories.contains("Outdated"),
284-
onToggle: {
285-
guard !filteredOutdated.isEmpty && !brewManager.isLoadingOutdated else { return }
286-
withAnimation(.easeInOut(duration: animationEnabled ? 0.3 : 0)) {
287-
toggleCategoryCollapse(for: "Outdated")
288-
}
289-
},
290-
isFirst: true,
291-
onPackageSelected: onPackageSelected,
292-
updatingPackages: updatingPackages,
293-
brewManager: brewManager,
294-
onUpdateAll: filteredOutdated.count > 1 ? {
295-
updateAllOutdated(packages: filteredOutdated)
296-
} : nil,
297-
colorScheme: colorScheme,
298-
showOnlyInstalledOnRequest: $showOnlyInstalledOnRequest
299-
)
288+
if !filteredOutdated.isEmpty {
289+
InstalledCategoryView(
290+
category: .outdated,
291+
packages: filteredOutdated,
292+
isLoading: brewManager.isLoadingOutdated,
293+
collapsed: installedCollapsedCategories.contains("Outdated"),
294+
onToggle: {
295+
guard !filteredOutdated.isEmpty && !brewManager.isLoadingOutdated else { return }
296+
withAnimation(.easeInOut(duration: animationEnabled ? 0.3 : 0)) {
297+
toggleCategoryCollapse(for: "Outdated", tab: .installed)
298+
}
299+
},
300+
isFirst: true,
301+
onPackageSelected: onPackageSelected,
302+
updatingPackages: updatingPackages,
303+
brewManager: brewManager,
304+
onUpdateAll: filteredOutdated.count > 1 ? {
305+
updateAllOutdated(packages: filteredOutdated)
306+
} : nil,
307+
colorScheme: colorScheme,
308+
showOnlyInstalledOnRequest: $showOnlyInstalledOnRequest
309+
)
310+
}
300311

301312
// Formulae category
302313
let formulaePackages = brewManager.installedByCategory[.formulae] ?? []
@@ -309,11 +320,11 @@ struct SearchInstallSection: View {
309320
category: .formulae,
310321
packages: filteredFormulae,
311322
isLoading: brewManager.isLoadingPackages,
312-
collapsed: filteredFormulae.isEmpty || collapsedCategories.contains("Formulae"),
323+
collapsed: installedCollapsedCategories.contains("Formulae"),
313324
onToggle: {
314325
guard !filteredFormulae.isEmpty && !brewManager.isLoadingPackages else { return }
315326
withAnimation(.easeInOut(duration: animationEnabled ? 0.3 : 0)) {
316-
toggleCategoryCollapse(for: "Formulae")
327+
toggleCategoryCollapse(for: "Formulae", tab: .installed)
317328
}
318329
},
319330
isFirst: false,
@@ -333,11 +344,11 @@ struct SearchInstallSection: View {
333344
category: .casks,
334345
packages: filteredCasks,
335346
isLoading: brewManager.isLoadingPackages,
336-
collapsed: filteredCasks.isEmpty || collapsedCategories.contains("Casks"),
347+
collapsed: installedCollapsedCategories.contains("Casks"),
337348
onToggle: {
338349
guard !filteredCasks.isEmpty && !brewManager.isLoadingPackages else { return }
339350
withAnimation(.easeInOut(duration: animationEnabled ? 0.3 : 0)) {
340-
toggleCategoryCollapse(for: "Casks")
351+
toggleCategoryCollapse(for: "Casks", tab: .installed)
341352
}
342353
},
343354
isFirst: false,
@@ -358,10 +369,10 @@ struct SearchInstallSection: View {
358369
AvailableCategoryView(
359370
category: .formulae,
360371
packages: filteredFormulae,
361-
collapsed: collapsedCategories.contains("Formulae"),
372+
collapsed: availableCollapsedCategories.contains("Formulae"),
362373
onToggle: {
363374
withAnimation(.easeInOut(duration: animationEnabled ? 0.3 : 0)) {
364-
toggleCategoryCollapse(for: "Formulae")
375+
toggleCategoryCollapse(for: "Formulae", tab: .available)
365376
}
366377
},
367378
isFirst: true,
@@ -380,10 +391,10 @@ struct SearchInstallSection: View {
380391
AvailableCategoryView(
381392
category: .casks,
382393
packages: filteredCasks,
383-
collapsed: collapsedCategories.contains("Casks"),
394+
collapsed: availableCollapsedCategories.contains("Casks"),
384395
onToggle: {
385396
withAnimation(.easeInOut(duration: animationEnabled ? 0.3 : 0)) {
386-
toggleCategoryCollapse(for: "Casks")
397+
toggleCategoryCollapse(for: "Casks", tab: .available)
387398
}
388399
},
389400
isFirst: false,
@@ -730,9 +741,9 @@ struct SearchResultRowView: View {
730741
.alert("Install \(result.name)?", isPresented: $showInstallAlert) {
731742
Button("Cancel", role: .cancel) { }
732743
Button("Install") {
733-
Task { @MainActor in
734-
isInstalling = true
735-
defer { isInstalling = false }
744+
Task {
745+
await MainActor.run { isInstalling = true }
746+
defer { Task { @MainActor in isInstalling = false } }
736747

737748
do {
738749
try await HomebrewController.shared.installPackage(name: result.name, cask: isCask)
@@ -753,17 +764,19 @@ struct SearchResultRowView: View {
753764
.alert("Update \(result.displayName ?? result.name)?", isPresented: $showUpdateAlert) {
754765
Button("Cancel", role: .cancel) { }
755766
Button("Update") {
756-
Task { @MainActor in
757-
isInstalling = true
758-
defer { isInstalling = false }
767+
Task {
768+
await MainActor.run { isInstalling = true }
769+
defer { Task { @MainActor in isInstalling = false } }
759770

760771
do {
761772
try await HomebrewController.shared.upgradePackage(name: result.name)
762773

763774
// Remove from outdated map immediately after successful update
764775
let shortName = result.name.components(separatedBy: "/").last ?? result.name
765-
brewManager.outdatedPackagesMap.removeValue(forKey: result.name)
766-
brewManager.outdatedPackagesMap.removeValue(forKey: shortName)
776+
await MainActor.run {
777+
brewManager.outdatedPackagesMap.removeValue(forKey: result.name)
778+
brewManager.outdatedPackagesMap.removeValue(forKey: shortName)
779+
}
767780

768781
await brewManager.loadInstalledPackages()
769782

@@ -787,20 +800,22 @@ struct SearchResultRowView: View {
787800
.alert("Uninstall \(result.displayName ?? result.name)?", isPresented: $showUninstallAlert) {
788801
Button("Cancel", role: .cancel) { }
789802
Button("Uninstall", role: .destructive) {
790-
Task { @MainActor in
791-
isUninstalling = true
792-
defer { isUninstalling = false }
803+
Task {
804+
await MainActor.run { isUninstalling = true }
805+
defer { Task { @MainActor in isUninstalling = false } }
793806

794807
do {
795808
try await HomebrewUninstaller.shared.uninstallPackage(name: result.name, cask: isCask, zap: true)
796809

797810
// Remove from installed lists instead of full refresh
798811
let shortName = result.name.components(separatedBy: "/").last ?? result.name
799812
if isCask {
800-
brewManager.installedCasks.removeAll { $0.name == result.name || $0.name == shortName }
813+
await MainActor.run {
814+
brewManager.installedCasks.removeAll { $0.name == result.name || $0.name == shortName }
815+
}
801816

802817
// Refresh AppState.sortedApps to remove uninstalled app (casks only)
803-
let folderPaths = FolderSettingsManager.shared.folderPaths
818+
let folderPaths = await MainActor.run { FolderSettingsManager.shared.folderPaths }
804819

805820
// Optimized: Only flush bundle for the uninstalled app (if still exists)
806821
if let matchingApp = findAppByCask(result.name) {
@@ -812,13 +827,19 @@ struct SearchResultRowView: View {
812827
invalidateCaskLookupCache()
813828
await loadAppsAsync(folderPaths: folderPaths)
814829
} else {
815-
brewManager.installedFormulae.removeAll { $0.name == result.name || $0.name == shortName }
830+
await MainActor.run {
831+
brewManager.installedFormulae.removeAll { $0.name == result.name || $0.name == shortName }
832+
}
833+
}
834+
await MainActor.run {
835+
brewManager.outdatedPackagesMap.removeValue(forKey: result.name)
836+
brewManager.outdatedPackagesMap.removeValue(forKey: shortName)
816837
}
817-
brewManager.outdatedPackagesMap.removeValue(forKey: result.name)
818-
brewManager.outdatedPackagesMap.removeValue(forKey: shortName)
819838

820839
// Refresh categorized view to update UI (for both casks and formulae)
821-
brewManager.updateInstalledCategories()
840+
await MainActor.run {
841+
brewManager.updateInstalledCategories()
842+
}
822843
} catch {
823844
printOS("Error uninstalling package \(result.name): \(error)")
824845
}
@@ -2434,9 +2455,9 @@ struct InstallButtonSection: View {
24342455
.alert("Install \(packageName)?", isPresented: $showInstallAlert) {
24352456
Button("Cancel", role: .cancel) { }
24362457
Button("Install") {
2437-
Task { @MainActor in
2438-
isInstalling = true
2439-
defer { isInstalling = false }
2458+
Task {
2459+
await MainActor.run { isInstalling = true }
2460+
defer { Task { @MainActor in isInstalling = false } }
24402461

24412462
do {
24422463
try await HomebrewController.shared.installPackage(name: packageName, cask: isCask)

0 commit comments

Comments
 (0)