Merge pull request #37 from RobotsAndPencils/enhancement/5-uininstall

Uninstall a xcode version
This commit is contained in:
Matt Kiazyk 2021-01-01 13:17:48 -06:00 committed by GitHub
commit 59f78253f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 104 additions and 35 deletions

View file

@ -4,7 +4,7 @@ import Combine
import Path
import LegibleError
import KeychainAccess
import SwiftUI
import Path
import Version
class AppState: ObservableObject {
@ -12,6 +12,7 @@ class AppState: ObservableObject {
private let helperClient = HelperClient()
private var cancellables = Set<AnyCancellable>()
private var selectPublisher: AnyCancellable?
private var uninstallPublisher: AnyCancellable?
@Published var authenticationState: AuthenticationState = .unauthenticated
@Published var availableXcodes: [AvailableXcode] = [] {
@ -200,14 +201,32 @@ class AppState: ObservableObject {
.store(in: &cancellables)
}
// MARK: -
// MARK: - Install
func install(id: Xcode.ID) {
// TODO:
}
// MARK: - Uninstall
func uninstall(id: Xcode.ID) {
// TODO:
guard
let installedXcode = Current.files.installedXcodes(Path.root/"Applications").first(where: { $0.version == id }),
uninstallPublisher == nil
else { return }
uninstallPublisher = uninstallXcode(path: installedXcode.path)
.flatMap { [unowned self] _ in
self.updateSelectedXcodePath()
}
.sink(
receiveCompletion: { [unowned self] completion in
if case let .failure(error) = completion {
self.error = AlertContent(title: "Error uninstalling Xcode", message: error.legibleLocalizedDescription)
}
self.uninstallPublisher = nil
},
receiveValue: { _ in }
)
}
func reveal(id: Xcode.ID) {
@ -293,6 +312,20 @@ class AppState: ObservableObject {
}
}
private func uninstallXcode(path: Path) -> AnyPublisher<Void, Error> {
return Deferred {
Future { promise in
do {
try Current.files.trashItem(at: path.url)
promise(.success(()))
} catch {
promise(.failure(error))
}
}
}
.eraseToAnyPublisher()
}
// MARK: - Nested Types

View file

@ -9,6 +9,7 @@ struct XcodeCommands: Commands {
var body: some Commands {
CommandMenu("Xcode") {
Group {
InstallCommand()
Divider()
@ -17,6 +18,10 @@ struct XcodeCommands: Commands {
OpenCommand()
RevealCommand()
CopyPathCommand()
Divider()
UninstallCommand()
}
.environmentObject(appState)
}
@ -31,24 +36,15 @@ struct InstallButton: View {
let xcode: Xcode?
var body: some View {
Button(action: uninstallOrInstall) {
if let xcode = xcode {
Text(xcode.installed == true ? "Uninstall" : "Install")
.help(xcode.installed == true ? "Uninstall" : "Install")
} else {
Text("Install")
.help("Install")
}
Button(action: install) {
Text("Install")
.help("Install")
}
}
private func uninstallOrInstall() {
private func install() {
guard let xcode = xcode else { return }
if xcode.installed {
appState.xcodeBeingConfirmedForUninstallation = xcode
} else {
appState.install(id: xcode.id)
}
appState.install(id: xcode.id)
}
}
@ -91,6 +87,21 @@ struct OpenButton: View {
}
}
struct UninstallButton: View {
@EnvironmentObject var appState: AppState
let xcode: Xcode?
var body: some View {
Button(action: {
appState.xcodeBeingConfirmedForUninstallation = xcode
}) {
Text("Uninstall")
}
.foregroundColor(.red)
.help("Uninstall")
}
}
struct RevealButton: View {
@EnvironmentObject var appState: AppState
let xcode: Xcode?
@ -133,8 +144,8 @@ struct InstallCommand: View {
var body: some View {
InstallButton(xcode: selectedXcode.unwrapped)
.keyboardShortcut(selectedXcode.unwrapped?.installed == true ? "u" : "i", modifiers: [.command, .option])
.disabled(selectedXcode.unwrapped == nil)
.keyboardShortcut("i", modifiers: [.command, .option])
.disabled(selectedXcode.unwrapped?.installed == true)
}
}
@ -181,3 +192,14 @@ struct CopyPathCommand: View {
.disabled(selectedXcode.unwrapped?.installed != true)
}
}
struct UninstallCommand: View {
@EnvironmentObject var appState: AppState
@FocusedValue(\.selectedXcode) private var selectedXcode: SelectedXcode?
var body: some View {
UninstallButton(xcode: selectedXcode.unwrapped)
.keyboardShortcut("u", modifiers: [.command, .option])
.disabled(selectedXcode.unwrapped?.installed != true)
}
}

View file

@ -7,13 +7,18 @@ struct MainWindow: View {
@AppStorage("lastUpdated") private var lastUpdated: Double?
@SceneStorage("isShowingInfoPane") private var isShowingInfoPane = false
@SceneStorage("xcodeListCategory") private var category: XcodeListCategory = .all
var body: some View {
HSplitView {
XcodeListView(selectedXcodeID: $selectedXcodeID, searchText: searchText, category: category)
.frame(minWidth: 300)
.layoutPriority(1)
.alert(item: $appState.xcodeBeingConfirmedForUninstallation) { xcode in
Alert(title: Text("Uninstall Xcode \(xcode.description)?"),
message: Text("It will be moved to the Trash, but won't be emptied."),
primaryButton: .destructive(Text("Uninstall"), action: { self.appState.uninstall(id: xcode.id) }),
secondaryButton: .cancel(Text("Cancel")))
}
InfoPane(selectedXcodeID: selectedXcodeID)
.frame(minWidth: 300, maxWidth: .infinity)
.frame(width: isShowingInfoPane ? nil : 0)
@ -31,15 +36,6 @@ struct MainWindow: View {
message: Text(verbatim: error.message),
dismissButton: .default(Text("OK")))
}
/*
Removing this for now, because it's overriding the error alert that's being worked on above.
.alert(item: $appState.xcodeBeingConfirmedForUninstallation) { xcode in
Alert(title: Text("Uninstall Xcode \(xcode.description)?"),
message: Text("It will be moved to the Trash, but won't be emptied."),
primaryButton: .destructive(Text("Uninstall"), action: { self.appState.uninstall(id: xcode.id) }),
secondaryButton: .cancel(Text("Cancel")))
}
**/
.sheet(isPresented: $appState.secondFactorData.isNotNil) {
secondFactorView(appState.secondFactorData!)
.environmentObject(appState)

View file

@ -37,6 +37,9 @@ struct InfoPane: View {
OpenButton(xcode: xcode)
.help("Open")
Spacer()
UninstallButton(xcode: xcode)
}
} else {
InstallButton(xcode: xcode)
@ -49,7 +52,7 @@ struct InfoPane: View {
compatibility(for: xcode)
sdks(for: xcode)
compilers(for: xcode)
Spacer()
}
} else {
@ -250,6 +253,21 @@ struct InfoPane_Previews: PreviewProvider {
})
.previewDisplayName("Populated, Uninstalled")
InfoPane(selectedXcodeID: Version(major: 12, minor: 3, patch: 0))
.environmentObject(configure(AppState()) {
$0.allXcodes = [
.init(
version: Version(major: 12, minor: 3, patch: 0),
installState: .installed,
selected: false,
path: "/Applications/Xcode-12.3.0.app",
icon: nil,
sdks: nil,
compilers: nil)
]
})
.previewDisplayName("Basic, installed")
InfoPane(selectedXcodeID: nil)
.environmentObject(configure(AppState()) {
$0.allXcodes = [

View file

@ -26,15 +26,15 @@ struct XcodeListViewRow: View {
installControl(for: xcode)
}
.contextMenu {
InstallButton(xcode: xcode)
Divider()
if xcode.installed {
SelectButton(xcode: xcode)
OpenButton(xcode: xcode)
RevealButton(xcode: xcode)
CopyPathButton(xcode: xcode)
Divider()
UninstallButton(xcode: xcode)
} else {
InstallButton(xcode: xcode)
}
}
}