Skip to content

Commit

Permalink
v1.2.2 update
Browse files Browse the repository at this point in the history
- Add option to completely uninstall apps (--zap flag)
- Add option to view all updates (--greedy flag)
- Fix incorrect self uninstall paths
  • Loading branch information
milanvarady committed Oct 28, 2023
1 parent 6b58128 commit d93dc9c
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 84 deletions.
8 changes: 4 additions & 4 deletions Applite.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 8;
CURRENT_PROJECT_VERSION = 9;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"Applite/Preview Content\"";
DEVELOPMENT_TEAM = 9CLTNBW4Z3;
Expand All @@ -674,7 +674,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.2.1;
MARKETING_VERSION = 1.2.2;
PRODUCT_BUNDLE_IDENTIFIER = dev.aerolite.Applite;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand All @@ -692,7 +692,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 8;
CURRENT_PROJECT_VERSION = 9;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"Applite/Preview Content\"";
DEVELOPMENT_TEAM = 9CLTNBW4Z3;
Expand All @@ -709,7 +709,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.2.1;
MARKETING_VERSION = 1.2.2;
PRODUCT_BUNDLE_IDENTIFIER = dev.aerolite.Applite;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand Down
19 changes: 15 additions & 4 deletions Applite/Model/Cask Data/Cask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,12 @@ final class Cask: Identifiable, Decodable, Hashable, ObservableObject {
}

/// Uninstalls the cask
/// - Parameters:
/// - caskData: ``CaskData`` object
/// - zap: If true the app will be uninstalled completely using the brew --zap flag
/// - Returns: Bool - Whether the task has failed or not
@discardableResult
func uninstall(caskData: CaskData) async -> Bool {
func uninstall(caskData: CaskData, zap: Bool = false) async -> Bool {
defer {
resetProgressState(caskData: caskData)
}
Expand All @@ -161,8 +164,10 @@ final class Cask: Identifiable, Decodable, Hashable, ObservableObject {
caskData.busyCasks.insert(self)
}

let arguments: [String] = if zap { ["--zap", self.id] } else { [self.id] }

return await runBrewCommand(command: "uninstall",
arguments: [self.id],
arguments: arguments,
taskDescription: "Uninstalling",
notificationSuccess: String(localized:"\(self.name) successfully uninstalled"),
notificationFailure: "Failed to uninstall \(self.name)",
Expand Down Expand Up @@ -254,10 +259,13 @@ final class Cask: Identifiable, Decodable, Hashable, ObservableObject {

// Log and Notify
if result.didFail {
sendNotification(title: notificationFailure, reason: .failure)
Self.logger.error("Failed to run brew command \"\(command)\" with arguments \"\(arguments)\", output: \(result.output)")

sendNotification(title: notificationFailure, reason: .failure)
await MainActor.run { self.progressState = .failed(output: result.output) }
} else {
Self.logger.notice("Successfully run brew command \"\(command)\" with arguments \"\(arguments)\", output: \(result.output)")

sendNotification(title: notificationSuccess, reason: .success)
await MainActor.run { self.progressState = .success }
try? await Task.sleep(for: .seconds(2))
Expand Down Expand Up @@ -330,11 +338,14 @@ final class Cask: Identifiable, Decodable, Hashable, ObservableObject {
private func resetProgressState(caskData: CaskData) {
Task {
await MainActor.run {
// Only reset state if it's not failed
if case .failed(_) = self.progressState {
} else {
// Only reset state if it's not failed
self.progressState = .idle
caskData.busyCasks.remove(self)

// Filter busy casks to make sure
caskData.filterBusyCasks()
}
}
}
Expand Down
11 changes: 9 additions & 2 deletions Applite/Model/Cask Data/CaskData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@ final class CaskData: ObservableObject {
(casksByCategory, casksByCategoryCoupled) = fillCategoryDicts()
}

func refreshOutdatedApps() async -> Void {
let outdatedCaskIDs = await shell("\(BrewPaths.currentBrewExecutable) outdated --cask -q").output
func refreshOutdatedApps(greedy: Bool = false) async -> Void {
let outdatedCaskIDs = await shell("\(BrewPaths.currentBrewExecutable) outdated --cask \(greedy ? "-g" : "") -q").output
.components(separatedBy: "\n")
.filter({ $0.count > 0 }) // Remove empty strings
.map({ $0.trimmingCharacters(in: .whitespacesAndNewlines) }) // Trim whitespace
Expand All @@ -262,4 +262,11 @@ final class CaskData: ObservableObject {

Self.logger.info("Outdated apps refreshed")
}

/// Filters busy casks
func filterBusyCasks() {
self.busyCasks = self.busyCasks.filter {
$0.progressState != .idle
}
}
}
37 changes: 25 additions & 12 deletions Applite/Utilities/UninstallSelf.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,47 @@
//

import Foundation
import os

enum UninstallError: Error {
case fileError
}

/// This function will uninstall Applite and all it's related files
func uninstallSelf(deleteBrewCache: Bool) {
let logger = Logger()

logger.notice("Applite uninstallation stated. deleteBrewCache: \(deleteBrewCache)")

// Delete related files and cache
let command = """
rm -rf "~/Library/Application Support/\(Bundle.main.appName)";
rm -rf "~/Library/Application Support/\(Bundle.main.bundleIdentifier!)";
rm -rf ~/Library/Containers/\(Bundle.main.bundleIdentifier!);
rm -rf ~/Library/Caches/\(Bundle.main.appName);
rm -rf ~/Library/Caches/\(Bundle.main.bundleIdentifier!);
rm -rf ~/Library/\(Bundle.main.appName);
rm -rf ~/Library/Preferences/*\(Bundle.main.bundleIdentifier!)*.plist;
rm -rf "~/Library/Saved Application State/\(Bundle.main.bundleIdentifier!).savedState";
rm -rf ~/Library/SyncedPreferences/\(Bundle.main.bundleIdentifier!)*.plist;
rm -rf ~/Library/WebKit/\(Bundle.main.bundleIdentifier!);
rm -r "$HOME/Library/Application Support/\(Bundle.main.appName)";
rm -r "$HOME/Library/Application Support/\(Bundle.main.bundleIdentifier!)";
rm -r $HOME/Library/Containers/\(Bundle.main.bundleIdentifier!);
rm -r $HOME/Library/Caches/\(Bundle.main.appName);
rm -r $HOME/Library/Caches/\(Bundle.main.bundleIdentifier!);
rm -r $HOME/Library/\(Bundle.main.appName);
rm -r $HOME/Library/Preferences/*\(Bundle.main.bundleIdentifier!)*.plist;
rm -r "$HOME/Library/Saved Application State/\(Bundle.main.bundleIdentifier!).savedState";
rm -r $HOME/Library/SyncedPreferences/\(Bundle.main.bundleIdentifier!)*.plist;
rm -r $HOME/Library/WebKit/\(Bundle.main.bundleIdentifier!);
rm -r $HOME/Library/HTTPStorages/dev.aerolite.Applite
"""

shell(command)
logger.notice("Running command: \(command)")

let result = shell(command)


logger.notice("Uninstall result: \(result.output)")

// Homebrew cache
if deleteBrewCache {
shell("rm -rf ~/Library/Caches/Homebrew")
shell("rm -rf $HOME/Library/Caches/Homebrew")
}

logger.notice("Self destructing. Goodbye world!")

// Quit the app and remove it
let process = Process()
process.launchPath = "/bin/bash"
Expand Down
72 changes: 41 additions & 31 deletions Applite/Views/App Views/AppView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ struct AppView: View {
switch role {
case .installAndManage:
if cask.isInstalled {
OpenAndManageAppView(cask: cask, deleteButton: false, moreOptionsButton: true)
OpenAndManageAppView(cask: cask, deleteButton: false)
} else {
DownloadButton(cask: cask)
.padding(.trailing, 5)
Expand All @@ -134,7 +134,7 @@ struct AppView: View {
UpdateButton(cask: cask)

case .installed:
OpenAndManageAppView(cask: cask, deleteButton: true, moreOptionsButton: false)
OpenAndManageAppView(cask: cask, deleteButton: true)
.padding(.trailing, 5)
}
} else {
Expand Down Expand Up @@ -337,13 +337,14 @@ struct AppView: View {
private struct OpenAndManageAppView: View {
@StateObject var cask: Cask
let deleteButton: Bool
let moreOptionsButton: Bool

@EnvironmentObject var caskData: CaskData

@State var appNotFoundShowing = false
@State var showingPopover = false

@State private var isOptionKeyDown = false

var body: some View {
// Lauch app
Button("Open") {
Expand All @@ -364,38 +365,47 @@ struct AppView: View {
UninstallButton(cask: cask)
}

if moreOptionsButton {
// More options popover
Button() {
showingPopover = true
} label: {
Image(systemName: "chevron.down")
.padding(.vertical)
.contentShape(Rectangle())
}
.popover(isPresented: $showingPopover) {
VStack(alignment: .leading, spacing: 6) {
// Reinstall button
Button {
Task {
await cask.reinstall(caskData: caskData)
}
} label: {
Label("Reinstall", systemImage: "arrow.2.squarepath")
// More options popover
Button() {
showingPopover = true
} label: {
Image(systemName: "chevron.down")
.padding(.vertical)
.contentShape(Rectangle())
}
.popover(isPresented: $showingPopover) {
VStack(alignment: .leading, spacing: 6) {
// Reinstall button
Button {
Task {
await cask.reinstall(caskData: caskData)
}

// Uninstall button
Button(role: .destructive) {
Task {
await cask.uninstall(caskData: caskData)
}
} label: {
Label("Uninstall", systemImage: "trash")
} label: {
Label("Reinstall", systemImage: "arrow.2.squarepath")
}

// Uninstall button
Button(role: .destructive) {
Task {
await cask.uninstall(caskData: caskData)
}
} label: {
Label("Uninstall", systemImage: "trash")
.foregroundStyle(.red)
}

// Uninstall completely button
Button(role: .destructive) {
Task {
await cask.uninstall(caskData: caskData, zap: true)
}
} label: {
Label("Uninstall Completely", systemImage: "trash.fill")
.foregroundStyle(.red)
}
.padding(8)
.buttonStyle(.plain)
}
.padding(8)
.buttonStyle(.plain)
}
}
}
Expand Down
34 changes: 17 additions & 17 deletions Applite/Views/Commands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,26 @@ struct CommandsMenu: Commands {
SidebarCommands()

CommandGroup(replacing: .appInfo) {
Button("About \(Bundle.main.appName)") {
NSApplication.shared.orderFrontStandardAboutPanel(
options: [
NSApplication.AboutPanelOptionKey.credits: NSAttributedString(
string: "MIT Licence",
attributes: [
NSAttributedString.Key.font: NSFont.systemFont(
ofSize: NSFont.smallSystemFontSize)
]
),
NSApplication.AboutPanelOptionKey(
rawValue: "Copyright"
): "© 2023 Milán Várady"
Button("About \(Bundle.main.appName)") {
NSApplication.shared.orderFrontStandardAboutPanel(
options: [
NSApplication.AboutPanelOptionKey.credits: NSAttributedString(
string: "MIT Licence",
attributes: [
NSAttributedString.Key.font: NSFont.systemFont(
ofSize: NSFont.smallSystemFontSize)
]
)
}
}
),
NSApplication.AboutPanelOptionKey(
rawValue: "Copyright"
): "© 2023 Milán Várady"
]
)
}
}

CommandGroup(before: .systemServices) {
Button("Uninstall...") {
Button("Uninstall Applite...") {
openWindow(id: "uninstall-self")
}

Expand Down
23 changes: 14 additions & 9 deletions Applite/Views/Detail Views/ActiveTasksView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,22 @@ struct ActiveTasksView: View {
@EnvironmentObject var caskData: CaskData

var body: some View {
VStack {
if caskData.busyCasks.isEmpty {
Text("No Active Tasks")
.font(.title)
} else {
AppGridView(casks: Array(caskData.busyCasks), appRole: .update)
ScrollView {
VStack {
if caskData.busyCasks.isEmpty {
Text("No Active Tasks")
.font(.title)
} else {
AppGridView(casks: Array(caskData.busyCasks), appRole: .update)
}

Spacer()
}

Spacer()
.padding()
}
.onAppear {
caskData.filterBusyCasks()
}
.padding()
}
}

Expand Down
Loading

0 comments on commit d93dc9c

Please sign in to comment.