mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2026-03-27 22:40:08 +01:00
✨ Allow for suspension of FSNotifier (for /homebrew/bin)
This commit is contained in:
@@ -10,6 +10,20 @@ import Foundation
|
||||
|
||||
extension App {
|
||||
|
||||
/**
|
||||
Performs a particular action while suspending the Homebrew watcher,
|
||||
until the task is completed.
|
||||
*/
|
||||
public func withSuspendedHomebrewWatcher<T>(_ action: () async throws -> T) async rethrows -> T {
|
||||
await suspendHomebrewWatcher()
|
||||
defer { resumeHomebrewWatcher() }
|
||||
return try await action()
|
||||
}
|
||||
|
||||
/**
|
||||
Prepares the `homebrew/bin` directory watcher. This allows PHP Monitor to quickly respond to
|
||||
external `brew` changes executed by the user.
|
||||
*/
|
||||
public func prepareHomebrewWatchers() {
|
||||
let notifier = FSNotifier(
|
||||
for: URL(fileURLWithPath: container.paths.binPath),
|
||||
@@ -21,6 +35,15 @@ extension App {
|
||||
self.debouncers["homebrewBinaries"] = Debouncer()
|
||||
}
|
||||
|
||||
private func suspendHomebrewWatcher() async {
|
||||
watchers["homebrewBinaries"]?.suspend()
|
||||
await debouncers["homebrewBinaries"]?.cancel()
|
||||
}
|
||||
|
||||
private func resumeHomebrewWatcher() {
|
||||
watchers["homebrewBinaries"]?.resume()
|
||||
}
|
||||
|
||||
public func onHomebrewPhpModification() async {
|
||||
if let debouncer = self.debouncers["homebrewBinaries"] {
|
||||
await debouncer.debounce(for: 5.0) {
|
||||
|
||||
@@ -22,6 +22,8 @@ class FSNotifier {
|
||||
private var fileDescriptor: CInt = -1
|
||||
private var dispatchSource: DispatchSourceFileSystemObject?
|
||||
|
||||
private var isSuspended = false
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
init(for url: URL, eventMask: DispatchSource.FileSystemEvent, onChange: @escaping () -> Void) {
|
||||
@@ -42,6 +44,10 @@ class FSNotifier {
|
||||
|
||||
dispatchSource?.setEventHandler(handler: {
|
||||
self.queue.async {
|
||||
// If our notifier is suspended, don't fire
|
||||
guard !self.isSuspended else { return }
|
||||
|
||||
// If our notifier is not suspended, fire
|
||||
Task { onChange() }
|
||||
}
|
||||
})
|
||||
@@ -57,6 +63,20 @@ class FSNotifier {
|
||||
dispatchSource?.resume()
|
||||
}
|
||||
|
||||
func suspend() {
|
||||
self.queue.async {
|
||||
self.isSuspended = true
|
||||
Log.perf("FSNotifier for \(self.url) has been suspended.")
|
||||
}
|
||||
}
|
||||
|
||||
func resume() {
|
||||
self.queue.async {
|
||||
self.isSuspended = false
|
||||
Log.perf("FSNotifier for \(self.url) has been resumed.")
|
||||
}
|
||||
}
|
||||
|
||||
func terminate() {
|
||||
dispatchSource?.cancel()
|
||||
}
|
||||
|
||||
@@ -10,8 +10,19 @@ import Foundation
|
||||
import SwiftUI
|
||||
|
||||
extension PhpVersionManagerView {
|
||||
|
||||
// MARK: - Variables
|
||||
|
||||
var hasUpdates: Bool {
|
||||
return self.formulae.phpVersions.contains { formula in
|
||||
return formula.hasUpgrade
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Executing Homebrew Commands
|
||||
|
||||
public func runCommand(_ command: ModifyPhpVersionCommand) async {
|
||||
if App.shared.container.phpEnvs.isBusy {
|
||||
if container.phpEnvs.isBusy {
|
||||
self.presentErrorAlert(
|
||||
title: "phpman.action_prevented_busy.title".localized,
|
||||
description: "phpman.action_prevented_busy.desc".localized,
|
||||
@@ -22,7 +33,8 @@ extension PhpVersionManagerView {
|
||||
|
||||
do {
|
||||
self.setBusyStatus(true)
|
||||
try await command.execute(shell: App.shared.container.shell) { progress in
|
||||
try await App.shared.withSuspendedHomebrewWatcher {
|
||||
try await command.execute(shell: container.shell) { progress in
|
||||
Task { @MainActor in
|
||||
self.status.title = progress.title
|
||||
self.status.description = progress.description
|
||||
@@ -36,8 +48,10 @@ extension PhpVersionManagerView {
|
||||
}
|
||||
// Finally, after completing the command, also refresh PHP versions
|
||||
await self.handler.refreshPhpVersions(loadOutdated: false)
|
||||
|
||||
// and mark the app as no longer busy
|
||||
self.setBusyStatus(false)
|
||||
}
|
||||
} catch let error {
|
||||
let error = error as! BrewCommandError
|
||||
let messages = error.log.suffix(2).joined(separator: "\n")
|
||||
@@ -80,9 +94,57 @@ extension PhpVersionManagerView {
|
||||
))
|
||||
}
|
||||
|
||||
public func uninstall(_ formula: BrewPhpFormula) async {
|
||||
let command = RemovePhpVersionCommand(container, formula: formula.name)
|
||||
|
||||
do {
|
||||
self.setBusyStatus(true)
|
||||
try await App.shared.withSuspendedHomebrewWatcher {
|
||||
try await command.execute(shell: container.shell) { progress in
|
||||
Task { @MainActor in
|
||||
self.status.title = progress.title
|
||||
self.status.description = progress.description
|
||||
self.status.busy = progress.value != 1
|
||||
|
||||
if progress.value == 1 {
|
||||
await self.handler.refreshPhpVersions(loadOutdated: false)
|
||||
self.setBusyStatus(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
self.setBusyStatus(false)
|
||||
await self.handler.refreshPhpVersions(loadOutdated: false)
|
||||
|
||||
self.presentErrorAlert(
|
||||
title: "phpman.failures.uninstall.title".localized,
|
||||
description: "phpman.failures.uninstall.desc".localized(
|
||||
"brew uninstall \(formula.name) --force"
|
||||
),
|
||||
button: "generic.ok".localized
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: GUI
|
||||
|
||||
/**
|
||||
Mark the PHP Version Manager, as well as the PHP environments as busy.
|
||||
*/
|
||||
public func setBusyStatus(_ busy: Bool) {
|
||||
Task { @MainActor in
|
||||
container.phpEnvs.isBusy = busy
|
||||
self.status.busy = busy
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Ask the user to confirm the uninstall of a particular PHP version.
|
||||
*/
|
||||
public func confirmUninstall(_ formula: BrewPhpFormula) async {
|
||||
// Disallow removal of the currently active versipn
|
||||
if formula.installedVersion == App.shared.container.phpEnvs.currentInstall?.version.text {
|
||||
// Disallow removal of the currently active version
|
||||
if formula.installedVersion == container.phpEnvs.currentInstall?.version.text {
|
||||
self.presentErrorAlert(
|
||||
title: "phpman.uninstall_prevented.title".localized,
|
||||
description: "phpman.uninstall_prevented.desc".localized,
|
||||
@@ -105,42 +167,9 @@ extension PhpVersionManagerView {
|
||||
)
|
||||
}
|
||||
|
||||
public func uninstall(_ formula: BrewPhpFormula) async {
|
||||
let command = RemovePhpVersionCommand(container, formula: formula.name)
|
||||
|
||||
do {
|
||||
self.setBusyStatus(true)
|
||||
try await command.execute(shell: App.shared.container.shell) { progress in
|
||||
Task { @MainActor in
|
||||
self.status.title = progress.title
|
||||
self.status.description = progress.description
|
||||
self.status.busy = progress.value != 1
|
||||
|
||||
if progress.value == 1 {
|
||||
await self.handler.refreshPhpVersions(loadOutdated: false)
|
||||
self.setBusyStatus(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
self.setBusyStatus(false)
|
||||
self.presentErrorAlert(
|
||||
title: "phpman.failures.uninstall.title".localized,
|
||||
description: "phpman.failures.uninstall.desc".localized(
|
||||
"brew uninstall \(formula.name) --force"
|
||||
),
|
||||
button: "generic.ok".localized
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public func setBusyStatus(_ busy: Bool) {
|
||||
Task { @MainActor in
|
||||
App.shared.container.phpEnvs.isBusy = busy
|
||||
self.status.busy = busy
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Present a generic error alert attached to the window.
|
||||
*/
|
||||
public func presentErrorAlert(
|
||||
title: String,
|
||||
description: String,
|
||||
@@ -158,9 +187,4 @@ extension PhpVersionManagerView {
|
||||
)
|
||||
}
|
||||
|
||||
var hasUpdates: Bool {
|
||||
return self.formulae.phpVersions.contains { formula in
|
||||
return formula.hasUpgrade
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +217,9 @@ struct PhpVersionManagerView: View {
|
||||
HStack {
|
||||
if !formula.healthy {
|
||||
Button("phpman.buttons.repair".localizedForSwiftUI, role: .destructive) {
|
||||
Task { await self.repairAll() }
|
||||
Task {
|
||||
await self.repairAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,11 +229,15 @@ struct PhpVersionManagerView: View {
|
||||
})
|
||||
} else if formula.isInstalled {
|
||||
Button("phpman.buttons.uninstall".localizedForSwiftUI, role: .destructive) {
|
||||
Task { await self.confirmUninstall(formula) }
|
||||
Task {
|
||||
await self.confirmUninstall(formula)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button("phpman.buttons.install".localizedForSwiftUI) {
|
||||
Task { await self.install(formula) }
|
||||
Task {
|
||||
await self.install(formula)
|
||||
}
|
||||
}.disabled(formula.hasUpgradedFormulaAlias || !formula.hasFormulaFile)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user