mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 07:19:04 +00:00
v1.3 and v1.4
This commit is contained in:
@@ -9,6 +9,9 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
C7045A282B03E71D00376976 /* MiniMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A272B03E71D00376976 /* MiniMode.swift */; };
|
C7045A282B03E71D00376976 /* MiniMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A272B03E71D00376976 /* MiniMode.swift */; };
|
||||||
C7045A2A2B03FAF000376976 /* TopBarMini.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A292B03FAF000376976 /* TopBarMini.swift */; };
|
C7045A2A2B03FAF000376976 /* TopBarMini.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A292B03FAF000376976 /* TopBarMini.swift */; };
|
||||||
|
C7045A322B068AD700376976 /* NewWin.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A312B068AD700376976 /* NewWin.swift */; };
|
||||||
|
C7045A342B068B2900376976 /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A332B068B2900376976 /* UpdateView.swift */; };
|
||||||
|
C7045A362B068DD700376976 /* PermView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A352B068DD700376976 /* PermView.swift */; };
|
||||||
C72893052AFD42E600C8C1CD /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72893042AFD42E600C8C1CD /* DeepLink.swift */; };
|
C72893052AFD42E600C8C1CD /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72893042AFD42E600C8C1CD /* DeepLink.swift */; };
|
||||||
C72893122AFD51EA00C8C1CD /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72893112AFD51EA00C8C1CD /* main.swift */; };
|
C72893122AFD51EA00C8C1CD /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72893112AFD51EA00C8C1CD /* main.swift */; };
|
||||||
C7575D382B0181FC006A600A /* FileWatcher in Frameworks */ = {isa = PBXBuildFile; productRef = C7575D372B0181FC006A600A /* FileWatcher */; };
|
C7575D382B0181FC006A600A /* FileWatcher in Frameworks */ = {isa = PBXBuildFile; productRef = C7575D372B0181FC006A600A /* FileWatcher */; };
|
||||||
@@ -19,7 +22,6 @@
|
|||||||
C76D08512AF850F300D07867 /* About.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08502AF850F300D07867 /* About.swift */; };
|
C76D08512AF850F300D07867 /* About.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08502AF850F300D07867 /* About.swift */; };
|
||||||
C76D08552AF89CDE00D07867 /* AppListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08542AF89CDE00D07867 /* AppListView.swift */; };
|
C76D08552AF89CDE00D07867 /* AppListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08542AF89CDE00D07867 /* AppListView.swift */; };
|
||||||
C77B90042AF18E2E009CC655 /* PearcleanerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90032AF18E2E009CC655 /* PearcleanerApp.swift */; };
|
C77B90042AF18E2E009CC655 /* PearcleanerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90032AF18E2E009CC655 /* PearcleanerApp.swift */; };
|
||||||
C77B90062AF18E2E009CC655 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90052AF18E2E009CC655 /* ContentView.swift */; };
|
|
||||||
C77B90082AF18E2F009CC655 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C77B90072AF18E2F009CC655 /* Assets.xcassets */; };
|
C77B90082AF18E2F009CC655 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C77B90072AF18E2F009CC655 /* Assets.xcassets */; };
|
||||||
C77B90162AF19377009CC655 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90152AF19377009CC655 /* AppState.swift */; };
|
C77B90162AF19377009CC655 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90152AF19377009CC655 /* AppState.swift */; };
|
||||||
C77B90182AF19382009CC655 /* AppCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90172AF19382009CC655 /* AppCommands.swift */; };
|
C77B90182AF19382009CC655 /* AppCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90172AF19382009CC655 /* AppCommands.swift */; };
|
||||||
@@ -32,7 +34,6 @@
|
|||||||
C7D31D482AFEB23700C7ED9E /* TopBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D472AFEB23700C7ED9E /* TopBar.swift */; };
|
C7D31D482AFEB23700C7ED9E /* TopBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D472AFEB23700C7ED9E /* TopBar.swift */; };
|
||||||
C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D492AFEB26700C7ED9E /* AppListItems.swift */; };
|
C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D492AFEB26700C7ED9E /* AppListItems.swift */; };
|
||||||
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D502AFF00F300C7ED9E /* Locations.swift */; };
|
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D502AFF00F300C7ED9E /* Locations.swift */; };
|
||||||
C7D31D532B0048BE00C7ED9E /* TrashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D522B0048BE00C7ED9E /* TrashView.swift */; };
|
|
||||||
C7D31D552B008CB900C7ED9E /* Shapes.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D542B008CB900C7ED9E /* Shapes.swift */; };
|
C7D31D552B008CB900C7ED9E /* Shapes.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D542B008CB900C7ED9E /* Shapes.swift */; };
|
||||||
C7D31D592B00A49300C7ED9E /* DropTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D582B00A49300C7ED9E /* DropTarget.swift */; };
|
C7D31D592B00A49300C7ED9E /* DropTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D582B00A49300C7ED9E /* DropTarget.swift */; };
|
||||||
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F539372AF60865007DF1B2 /* Utilities.swift */; };
|
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F539372AF60865007DF1B2 /* Utilities.swift */; };
|
||||||
@@ -63,18 +64,19 @@
|
|||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
C7045A272B03E71D00376976 /* MiniMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiniMode.swift; sourceTree = "<group>"; };
|
C7045A272B03E71D00376976 /* MiniMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiniMode.swift; sourceTree = "<group>"; };
|
||||||
C7045A292B03FAF000376976 /* TopBarMini.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBarMini.swift; sourceTree = "<group>"; };
|
C7045A292B03FAF000376976 /* TopBarMini.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBarMini.swift; sourceTree = "<group>"; };
|
||||||
|
C7045A312B068AD700376976 /* NewWin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewWin.swift; sourceTree = "<group>"; };
|
||||||
|
C7045A332B068B2900376976 /* UpdateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateView.swift; sourceTree = "<group>"; };
|
||||||
|
C7045A352B068DD700376976 /* PermView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermView.swift; sourceTree = "<group>"; };
|
||||||
C72893042AFD42E600C8C1CD /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = "<group>"; };
|
C72893042AFD42E600C8C1CD /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = "<group>"; };
|
||||||
C728930F2AFD51EA00C8C1CD /* PearcleanerSentinel */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = PearcleanerSentinel; sourceTree = BUILT_PRODUCTS_DIR; };
|
C728930F2AFD51EA00C8C1CD /* PearcleanerSentinel */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = PearcleanerSentinel; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
C72893112AFD51EA00C8C1CD /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
C72893112AFD51EA00C8C1CD /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||||
C76D08472AF83C3F00D07867 /* Update.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Update.swift; sourceTree = "<group>"; };
|
C76D08472AF83C3F00D07867 /* Update.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Update.swift; sourceTree = "<group>"; };
|
||||||
C76D08492AF83C6E00D07867 /* General.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = General.swift; sourceTree = "<group>"; };
|
C76D08492AF83C6E00D07867 /* General.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = General.swift; sourceTree = "<group>"; };
|
||||||
C76D084B2AF83C9D00D07867 /* Permissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Permissions.swift; sourceTree = "<group>"; };
|
C76D084B2AF83C9D00D07867 /* Permissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Permissions.swift; sourceTree = "<group>"; };
|
||||||
C76D084D2AF84D8C00D07867 /* Builds */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Builds; sourceTree = "<group>"; };
|
|
||||||
C76D08502AF850F300D07867 /* About.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = About.swift; sourceTree = "<group>"; };
|
C76D08502AF850F300D07867 /* About.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = About.swift; sourceTree = "<group>"; };
|
||||||
C76D08542AF89CDE00D07867 /* AppListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListView.swift; sourceTree = "<group>"; };
|
C76D08542AF89CDE00D07867 /* AppListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListView.swift; sourceTree = "<group>"; };
|
||||||
C77B90002AF18E2E009CC655 /* Pearcleaner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pearcleaner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
C77B90002AF18E2E009CC655 /* Pearcleaner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pearcleaner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
C77B90032AF18E2E009CC655 /* PearcleanerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PearcleanerApp.swift; sourceTree = "<group>"; };
|
C77B90032AF18E2E009CC655 /* PearcleanerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PearcleanerApp.swift; sourceTree = "<group>"; };
|
||||||
C77B90052AF18E2E009CC655 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
|
||||||
C77B90072AF18E2F009CC655 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
C77B90072AF18E2F009CC655 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
C77B90142AF18E7C009CC655 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
C77B90142AF18E7C009CC655 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
C77B90152AF19377009CC655 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
|
C77B90152AF19377009CC655 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
|
||||||
@@ -90,7 +92,6 @@
|
|||||||
C7D31D472AFEB23700C7ED9E /* TopBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBar.swift; sourceTree = "<group>"; };
|
C7D31D472AFEB23700C7ED9E /* TopBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBar.swift; sourceTree = "<group>"; };
|
||||||
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListItems.swift; sourceTree = "<group>"; };
|
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListItems.swift; sourceTree = "<group>"; };
|
||||||
C7D31D502AFF00F300C7ED9E /* Locations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Locations.swift; sourceTree = "<group>"; };
|
C7D31D502AFF00F300C7ED9E /* Locations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Locations.swift; sourceTree = "<group>"; };
|
||||||
C7D31D522B0048BE00C7ED9E /* TrashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrashView.swift; sourceTree = "<group>"; };
|
|
||||||
C7D31D542B008CB900C7ED9E /* Shapes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shapes.swift; sourceTree = "<group>"; };
|
C7D31D542B008CB900C7ED9E /* Shapes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shapes.swift; sourceTree = "<group>"; };
|
||||||
C7D31D582B00A49300C7ED9E /* DropTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropTarget.swift; sourceTree = "<group>"; };
|
C7D31D582B00A49300C7ED9E /* DropTarget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropTarget.swift; sourceTree = "<group>"; };
|
||||||
C7F539372AF60865007DF1B2 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
|
C7F539372AF60865007DF1B2 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
|
||||||
@@ -115,6 +116,16 @@
|
|||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
C7045A302B068ABE00376976 /* Windows */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C7045A312B068AD700376976 /* NewWin.swift */,
|
||||||
|
C7045A332B068B2900376976 /* UpdateView.swift */,
|
||||||
|
C7045A352B068DD700376976 /* PermView.swift */,
|
||||||
|
);
|
||||||
|
path = Windows;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C72893072AFD442600C8C1CD /* Logic */ = {
|
C72893072AFD442600C8C1CD /* Logic */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -174,7 +185,6 @@
|
|||||||
C77B8FF72AF18E2E009CC655 = {
|
C77B8FF72AF18E2E009CC655 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C76D084D2AF84D8C00D07867 /* Builds */,
|
|
||||||
C77B90022AF18E2E009CC655 /* Pearcleaner */,
|
C77B90022AF18E2E009CC655 /* Pearcleaner */,
|
||||||
C72893102AFD51EA00C8C1CD /* PearcleanerSentinel */,
|
C72893102AFD51EA00C8C1CD /* PearcleanerSentinel */,
|
||||||
C77B90012AF18E2E009CC655 /* Products */,
|
C77B90012AF18E2E009CC655 /* Products */,
|
||||||
@@ -198,6 +208,7 @@
|
|||||||
C76D084F2AF84E0100D07867 /* Resources */,
|
C76D084F2AF84E0100D07867 /* Resources */,
|
||||||
C76D08462AF83C2F00D07867 /* Settings */,
|
C76D08462AF83C2F00D07867 /* Settings */,
|
||||||
C77B901F2AF1B390009CC655 /* Views */,
|
C77B901F2AF1B390009CC655 /* Views */,
|
||||||
|
C7045A302B068ABE00376976 /* Windows */,
|
||||||
C72893072AFD442600C8C1CD /* Logic */,
|
C72893072AFD442600C8C1CD /* Logic */,
|
||||||
);
|
);
|
||||||
path = Pearcleaner;
|
path = Pearcleaner;
|
||||||
@@ -206,13 +217,11 @@
|
|||||||
C77B901F2AF1B390009CC655 /* Views */ = {
|
C77B901F2AF1B390009CC655 /* Views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C77B90052AF18E2E009CC655 /* ContentView.swift */,
|
|
||||||
C77B90222AF2D616009CC655 /* FilesView.swift */,
|
C77B90222AF2D616009CC655 /* FilesView.swift */,
|
||||||
C76D08542AF89CDE00D07867 /* AppListView.swift */,
|
C76D08542AF89CDE00D07867 /* AppListView.swift */,
|
||||||
C7D31D472AFEB23700C7ED9E /* TopBar.swift */,
|
C7D31D472AFEB23700C7ED9E /* TopBar.swift */,
|
||||||
C7045A292B03FAF000376976 /* TopBarMini.swift */,
|
C7045A292B03FAF000376976 /* TopBarMini.swift */,
|
||||||
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */,
|
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */,
|
||||||
C7D31D522B0048BE00C7ED9E /* TrashView.swift */,
|
|
||||||
C7D31D582B00A49300C7ED9E /* DropTarget.swift */,
|
C7D31D582B00A49300C7ED9E /* DropTarget.swift */,
|
||||||
C7045A272B03E71D00376976 /* MiniMode.swift */,
|
C7045A272B03E71D00376976 /* MiniMode.swift */,
|
||||||
);
|
);
|
||||||
@@ -335,13 +344,13 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
C76D08482AF83C3F00D07867 /* Update.swift in Sources */,
|
C76D08482AF83C3F00D07867 /* Update.swift in Sources */,
|
||||||
|
C7045A342B068B2900376976 /* UpdateView.swift in Sources */,
|
||||||
C77B90162AF19377009CC655 /* AppState.swift in Sources */,
|
C77B90162AF19377009CC655 /* AppState.swift in Sources */,
|
||||||
|
C7045A322B068AD700376976 /* NewWin.swift in Sources */,
|
||||||
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */,
|
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */,
|
||||||
C77B901A2AF1938F009CC655 /* Logic.swift in Sources */,
|
C77B901A2AF1938F009CC655 /* Logic.swift in Sources */,
|
||||||
C7D31D532B0048BE00C7ED9E /* TrashView.swift in Sources */,
|
|
||||||
C7D31D462AFDA90C00C7ED9E /* Sentinel.swift in Sources */,
|
C7D31D462AFDA90C00C7ED9E /* Sentinel.swift in Sources */,
|
||||||
C7D31D592B00A49300C7ED9E /* DropTarget.swift in Sources */,
|
C7D31D592B00A49300C7ED9E /* DropTarget.swift in Sources */,
|
||||||
C77B90062AF18E2E009CC655 /* ContentView.swift in Sources */,
|
|
||||||
C7045A2A2B03FAF000376976 /* TopBarMini.swift in Sources */,
|
C7045A2A2B03FAF000376976 /* TopBarMini.swift in Sources */,
|
||||||
C77B901C2AF193A3009CC655 /* Styles.swift in Sources */,
|
C77B901C2AF193A3009CC655 /* Styles.swift in Sources */,
|
||||||
C7045A282B03E71D00376976 /* MiniMode.swift in Sources */,
|
C7045A282B03E71D00376976 /* MiniMode.swift in Sources */,
|
||||||
@@ -350,6 +359,7 @@
|
|||||||
C76D084A2AF83C6E00D07867 /* General.swift in Sources */,
|
C76D084A2AF83C6E00D07867 /* General.swift in Sources */,
|
||||||
C77B90042AF18E2E009CC655 /* PearcleanerApp.swift in Sources */,
|
C77B90042AF18E2E009CC655 /* PearcleanerApp.swift in Sources */,
|
||||||
C7D31D552B008CB900C7ED9E /* Shapes.swift in Sources */,
|
C7D31D552B008CB900C7ED9E /* Shapes.swift in Sources */,
|
||||||
|
C7045A362B068DD700376976 /* PermView.swift in Sources */,
|
||||||
C76D08512AF850F300D07867 /* About.swift in Sources */,
|
C76D08512AF850F300D07867 /* About.swift in Sources */,
|
||||||
C77B90182AF19382009CC655 /* AppCommands.swift in Sources */,
|
C77B90182AF19382009CC655 /* AppCommands.swift in Sources */,
|
||||||
C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */,
|
C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */,
|
||||||
@@ -525,7 +535,7 @@
|
|||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 3;
|
CURRENT_PROJECT_VERSION = 5;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = BK8443AXLU;
|
DEVELOPMENT_TEAM = BK8443AXLU;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -534,15 +544,15 @@
|
|||||||
INFOPLIST_FILE = Pearcleaner/Resources/Info.plist;
|
INFOPLIST_FILE = Pearcleaner/Resources/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Pearcleaner;
|
INFOPLIST_KEY_CFBundleDisplayName = Pearcleaner;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||||
INFOPLIST_KEY_NSAppleEventsUsageDescription = "App needs to send apple events to FInder to delete files";
|
INFOPLIST_KEY_NSAppleEventsUsageDescription = "Pearcleaner needs to send apple events to FInder to delete files";
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "Requires full disk access to be able to delete files in locations with no permission";
|
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "Pearcleaner requires full disk access to be able to delete files in locations with no permission";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.2;
|
MARKETING_VERSION = 1.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner;
|
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
@@ -559,7 +569,7 @@
|
|||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 3;
|
CURRENT_PROJECT_VERSION = 5;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = BK8443AXLU;
|
DEVELOPMENT_TEAM = BK8443AXLU;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -568,15 +578,15 @@
|
|||||||
INFOPLIST_FILE = Pearcleaner/Resources/Info.plist;
|
INFOPLIST_FILE = Pearcleaner/Resources/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Pearcleaner;
|
INFOPLIST_KEY_CFBundleDisplayName = Pearcleaner;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||||
INFOPLIST_KEY_NSAppleEventsUsageDescription = "App needs to send apple events to FInder to delete files";
|
INFOPLIST_KEY_NSAppleEventsUsageDescription = "Pearcleaner needs to send apple events to FInder to delete files";
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "Requires full disk access to be able to delete files in locations with no permission";
|
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "Pearcleaner requires full disk access to be able to delete files in locations with no permission";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.2;
|
MARKETING_VERSION = 1.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner;
|
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ struct AppCommands: Commands {
|
|||||||
Button {
|
Button {
|
||||||
loadGithubReleases(appState: appState, manual: true)
|
loadGithubReleases(appState: appState, manual: true)
|
||||||
} label: {
|
} label: {
|
||||||
Text("Updates")
|
Text("Check for Updates")
|
||||||
}
|
}
|
||||||
.keyboardShortcut("u", modifiers: .command)
|
.keyboardShortcut("u", modifiers: .command)
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,11 @@ class AppState: ObservableObject
|
|||||||
// @Published var currentMode = CurrentMode.regular
|
// @Published var currentMode = CurrentMode.regular
|
||||||
@Published var showAlert: Bool = false
|
@Published var showAlert: Bool = false
|
||||||
@Published var sidebar: Bool = true
|
@Published var sidebar: Bool = true
|
||||||
|
@Published var showPopover: Bool = false
|
||||||
@Published var isReminderVisible: Bool = false
|
@Published var isReminderVisible: Bool = false
|
||||||
@Published var releases = [Release]()
|
@Published var releases = [Release]()
|
||||||
|
@Published var progressBar: (String, Double) = ("Ready", 0.0)
|
||||||
|
|
||||||
|
|
||||||
//Window
|
//Window
|
||||||
// @Published var winWidth: CGFloat = 1020
|
// @Published var winWidth: CGFloat = 1020
|
||||||
@@ -35,7 +38,8 @@ class AppState: ObservableObject
|
|||||||
bundleIdentifier: "",
|
bundleIdentifier: "",
|
||||||
appName: "",
|
appName: "",
|
||||||
appVersion: "",
|
appVersion: "",
|
||||||
appIcon: nil
|
appIcon: nil,
|
||||||
|
webApp: false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,8 +51,9 @@ struct AppInfo: Identifiable, Hashable {
|
|||||||
let appName: String
|
let appName: String
|
||||||
let appVersion: String
|
let appVersion: String
|
||||||
let appIcon: NSImage?
|
let appIcon: NSImage?
|
||||||
|
let webApp: Bool
|
||||||
|
|
||||||
static let empty = AppInfo(id: UUID(), path: URL(fileURLWithPath: ""), bundleIdentifier: "", appName: "", appVersion: "", appIcon: nil)
|
static let empty = AppInfo(id: UUID(), path: URL(fileURLWithPath: ""), bundleIdentifier: "", appName: "", appVersion: "", appIcon: nil, webApp: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -78,11 +83,13 @@ enum CurrentDetailsView:Int
|
|||||||
case apps
|
case apps
|
||||||
}
|
}
|
||||||
|
|
||||||
//enum CurrentMode:Int
|
enum NewWindow:Int
|
||||||
//{
|
{
|
||||||
// case regular
|
case update
|
||||||
// case mini
|
case no_update
|
||||||
//}
|
case perm
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
enum AlertType:Int
|
enum AlertType:Int
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ func getAppInfo(atPath path: URL) -> AppInfo? {
|
|||||||
var appIconFileName = bundle.infoDictionary?["CFBundleIconFile"] as? String {
|
var appIconFileName = bundle.infoDictionary?["CFBundleIconFile"] as? String {
|
||||||
var appIcon: NSImage?
|
var appIcon: NSImage?
|
||||||
var appName: String?
|
var appName: String?
|
||||||
|
var webApp: Bool?
|
||||||
if let localizedName = bundle.localizedInfoDictionary?[kCFBundleNameKey as String] as? String {
|
if let localizedName = bundle.localizedInfoDictionary?[kCFBundleNameKey as String] as? String {
|
||||||
appName = localizedName
|
appName = localizedName
|
||||||
} else if let bundleName = bundle.infoDictionary?["CFBundleName"] as? String {
|
} else if let bundleName = bundle.infoDictionary?["CFBundleName"] as? String {
|
||||||
@@ -111,7 +112,13 @@ func getAppInfo(atPath path: URL) -> AppInfo? {
|
|||||||
print("App Icon not found for app at path: \(path)")
|
print("App Icon not found for app at path: \(path)")
|
||||||
}
|
}
|
||||||
|
|
||||||
return AppInfo(id: UUID(), path: path, bundleIdentifier: bundleIdentifier, appName: appName ?? "", appVersion: appVersion, appIcon: appIcon)
|
if bundle.infoDictionary?["LSTemplateApplication"] is Bool {
|
||||||
|
webApp = true
|
||||||
|
} else {
|
||||||
|
webApp = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppInfo(id: UUID(), path: path, bundleIdentifier: bundleIdentifier, appName: appName ?? "", appVersion: appVersion, appIcon: appIcon, webApp: webApp ?? false)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
print("One or more variables missing for app at path: \(path)")
|
print("One or more variables missing for app at path: \(path)")
|
||||||
@@ -178,6 +185,9 @@ func findPathsForApp(appState: AppState, appInfo: AppInfo) {
|
|||||||
// appState.paths.insert(url, at: 0)
|
// appState.paths.insert(url, at: 0)
|
||||||
collection.insert(url, at: 0)
|
collection.insert(url, at: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only search for extra files if not a webapp
|
||||||
|
// if !appInfo.webApp {
|
||||||
let fileManager = FileManager.default
|
let fileManager = FileManager.default
|
||||||
let dispatchGroup = DispatchGroup()
|
let dispatchGroup = DispatchGroup()
|
||||||
let bundleComponents = appInfo.bundleIdentifier.components(separatedBy: ".")
|
let bundleComponents = appInfo.bundleIdentifier.components(separatedBy: ".")
|
||||||
@@ -203,20 +213,32 @@ func findPathsForApp(appState: AppState, appInfo: AppInfo) {
|
|||||||
if collection.contains(itemURL) {
|
if collection.contains(itemURL) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Catch web app plist files
|
||||||
|
if appInfo.webApp {
|
||||||
|
if itemL.contains(bundleIdentifierL) {
|
||||||
|
if collection.contains(itemURL) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
collection.append(itemURL)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Catch all the com.apple folders in the OS since there's a ton and probably unrelated
|
||||||
if itemL.contains("comapple") {
|
if itemL.contains("comapple") {
|
||||||
if itemL.contains(bundle) || itemL.contains(bundleIdentifierL) || (nameL.count > 4 && itemL.contains(nameL)) {
|
if itemL.contains(bundle) || itemL.contains(bundleIdentifierL) || (nameL.count > 4 && itemL.contains(nameL)) {
|
||||||
collection.append(itemURL)
|
collection.append(itemURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Catch Xcode files
|
||||||
else if itemL.contains("xcode") {
|
else if itemL.contains("xcode") {
|
||||||
if itemL.contains(bundle) || itemL.contains(bundleIdentifierL) || (nameL.count > 4 && itemL.contains(nameL)) {
|
if itemL.contains(bundle) || itemL.contains(bundleIdentifierL) || (nameL.count > 4 && itemL.contains(nameL)) {
|
||||||
collection.append(itemURL)
|
collection.append(itemURL)
|
||||||
}
|
}
|
||||||
|
// Catch Logitech files since Logi is part of word login and can return random files
|
||||||
} else if itemL.contains("logi") && !itemL.contains("login") {
|
} else if itemL.contains("logi") && !itemL.contains("login") {
|
||||||
if itemL.contains(bundleComponents[1]) || itemL.contains(bundle) || itemL.contains(bundleIdentifierL) || (nameL.count > 3 && itemL.contains(nameL)) {
|
if itemL.contains(bundleComponents[1]) || itemL.contains(bundle) || itemL.contains(bundleIdentifierL) || (nameL.count > 3 && itemL.contains(nameL)) {
|
||||||
collection.append(itemURL)
|
collection.append(itemURL)
|
||||||
}
|
}
|
||||||
|
// Catch MS Office files since they have many random folder names and very short names
|
||||||
} else if itemL.contains("office") || itemL.contains("oneauth") || itemL.suffix(2).contains("ms") || itemL.contains("onenote") {
|
} else if itemL.contains("office") || itemL.contains("oneauth") || itemL.suffix(2).contains("ms") || itemL.contains("onenote") {
|
||||||
if itemL.contains(bundle) || itemL.contains(bundleIdentifierL) || (nameL.count > 4 && itemL.contains(nameL)) {
|
if itemL.contains(bundle) || itemL.contains(bundleIdentifierL) || (nameL.count > 4 && itemL.contains(nameL)) {
|
||||||
collection.append(itemURL)
|
collection.append(itemURL)
|
||||||
@@ -227,6 +249,8 @@ func findPathsForApp(appState: AppState, appInfo: AppInfo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -241,6 +265,14 @@ func findPathsForApp(appState: AppState, appInfo: AppInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if appInfo.webApp {
|
||||||
|
// updateOnMain {
|
||||||
|
// appState.paths = collection
|
||||||
|
// appState.selectedItems = Set(collection)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+590
-590
File diff suppressed because it is too large
Load Diff
@@ -143,7 +143,10 @@ struct SimpleSearchStyle: TextFieldStyle {
|
|||||||
@State private var isHovered = false
|
@State private var isHovered = false
|
||||||
@State var icon: Image?
|
@State var icon: Image?
|
||||||
@State var trash: Bool = false
|
@State var trash: Bool = false
|
||||||
|
@Binding var reload: Bool
|
||||||
@Binding var text: String
|
@Binding var text: String
|
||||||
|
@EnvironmentObject var appState: AppState
|
||||||
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
|
|
||||||
func _body(configuration: TextField<Self._Label>) -> some View {
|
func _body(configuration: TextField<Self._Label>) -> some View {
|
||||||
|
|
||||||
@@ -156,21 +159,40 @@ struct SimpleSearchStyle: TextFieldStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
configuration
|
configuration
|
||||||
.frame(maxWidth: 300)
|
// .frame(minWidth: mini ? 80 : 300)
|
||||||
|
// .frame(maxWidth: mini ? 150 : 300)
|
||||||
.font(.title3)
|
.font(.title3)
|
||||||
.textFieldStyle(PlainTextFieldStyle())
|
.textFieldStyle(PlainTextFieldStyle())
|
||||||
if trash {
|
|
||||||
Image(systemName: "xmark")
|
Image(systemName: "arrow.triangle.2.circlepath")
|
||||||
.resizable()
|
.resizable()
|
||||||
.aspectRatio(contentMode: .fit)
|
.aspectRatio(contentMode: .fit)
|
||||||
.frame(width: 10, height: 10)
|
.frame(width: 15, height: 15)
|
||||||
.padding(.trailing, 5)
|
.padding(.trailing, 5)
|
||||||
.foregroundStyle(isHovered ? Color("mode").opacity(0.8) : Color("mode").opacity(0.5))
|
.foregroundStyle(isHovered ? Color("mode").opacity(0.8) : Color("mode").opacity(0.5))
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
text = ""
|
// Refresh Apps list
|
||||||
|
reload.toggle()
|
||||||
|
let sortedApps = getSortedApps()
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||||
|
appState.sortedApps.userApps = sortedApps.userApps
|
||||||
|
appState.sortedApps.systemApps = sortedApps.systemApps
|
||||||
|
reload.toggle()
|
||||||
}
|
}
|
||||||
.opacity(text.isEmpty ? 0 : 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if trash {
|
||||||
|
// Image(systemName: "xmark")
|
||||||
|
// .resizable()
|
||||||
|
// .aspectRatio(contentMode: .fit)
|
||||||
|
// .frame(width: 10, height: 10)
|
||||||
|
// .padding(.trailing, 5)
|
||||||
|
// .foregroundStyle(isHovered ? Color("mode").opacity(0.8) : Color("mode").opacity(0.5))
|
||||||
|
// .onTapGesture {
|
||||||
|
// text = ""
|
||||||
|
// }
|
||||||
|
// .opacity(text.isEmpty ? 0 : 1)
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -242,9 +264,9 @@ struct WindowActionButton: ButtonStyle {
|
|||||||
.background(
|
.background(
|
||||||
!configuration.isPressed ?
|
!configuration.isPressed ?
|
||||||
!hovered ?
|
!hovered ?
|
||||||
action == .accept ? Color("AccentColor") : Color.red :
|
action == .accept ? Color("button") : Color.red :
|
||||||
action == .accept ? Color("AccentColor").opacity(0.8) : Color.red.opacity(0.8) :
|
action == .accept ? Color("button").opacity(0.8) : Color.red.opacity(0.8) :
|
||||||
action == .accept ? Color("AccentColor").opacity(0.5) : Color.red.opacity(0.5)
|
action == .accept ? Color("button").opacity(0.5) : Color.red.opacity(0.5)
|
||||||
)
|
)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.cornerRadius(8)
|
.cornerRadius(8)
|
||||||
|
|||||||
@@ -25,35 +25,98 @@ func updateOnBackground(_ updates: @escaping () -> Void) {
|
|||||||
|
|
||||||
|
|
||||||
// Resize window
|
// Resize window
|
||||||
func resizeWindow() {
|
func resizeWindow(width: CGFloat, height: CGFloat) {
|
||||||
if let window = NSApplication.shared.windows.first {
|
if let window = NSApplication.shared.windows.first {
|
||||||
let newSize = NSSize(width: 500, height: 450)
|
let newSize = NSSize(width: width, height: height)
|
||||||
window.setContentSize(newSize)
|
window.setContentSize(newSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check FDA
|
||||||
|
func checkFullDiskAccessForApp() -> Bool {
|
||||||
|
let process = Process()
|
||||||
|
process.launchPath = "/usr/bin/sqlite3"
|
||||||
|
process.arguments = ["/Library/Application Support/com.apple.TCC/TCC.db", "select client from access where auth_value and service = \"kTCCServiceSystemPolicyAllFiles\" and client = \"com.alienator88.Pearcleaner\""]
|
||||||
|
|
||||||
|
let pipe = Pipe()
|
||||||
|
let pipeErr = Pipe()
|
||||||
|
process.standardOutput = pipe
|
||||||
|
process.standardError = pipeErr
|
||||||
|
process.launch()
|
||||||
|
|
||||||
|
// let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||||
|
let dataErr = pipeErr.fileHandleForReading.readDataToEndOfFile()
|
||||||
|
|
||||||
|
let output = String(data: dataErr, encoding: .utf8)
|
||||||
|
|
||||||
|
// Check if the app is in the results
|
||||||
|
if let result = output, result.isEmpty {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Check for access to Full Disk access
|
// Check for access to Full Disk access
|
||||||
func checkAndRequestFullDiskAccess(appState: AppState, skipAlert: Bool = false) -> Bool {
|
func checkAndRequestFullDiskAccess(appState: AppState, skipAlert: Bool = false) -> Bool {
|
||||||
@AppStorage("settings.permissions.disk") var diskP: Bool = false
|
@AppStorage("settings.permissions.disk") var diskP: Bool = false
|
||||||
|
|
||||||
let fileURL = URL(fileURLWithPath: "\(home)/Library/Application Support/com.apple.TCC/TCC.db")
|
let process = Process()
|
||||||
|
process.launchPath = "/usr/bin/sqlite3"
|
||||||
|
process.arguments = ["/Library/Application Support/com.apple.TCC/TCC.db", "select client from access where auth_value and service = \"kTCCServiceSystemPolicyAllFiles\" and client = \"com.alienator88.Pearcleaner\""]
|
||||||
|
|
||||||
let accessStatus = FileManager.default.isWritableFile(atPath: fileURL.path)
|
let pipe = Pipe()
|
||||||
if accessStatus {
|
let pipeErr = Pipe()
|
||||||
|
process.standardOutput = pipe
|
||||||
|
process.standardError = pipeErr
|
||||||
|
process.launch()
|
||||||
|
|
||||||
|
// let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||||
|
let dataErr = pipeErr.fileHandleForReading.readDataToEndOfFile()
|
||||||
|
|
||||||
|
let output = String(data: dataErr, encoding: .utf8)
|
||||||
|
|
||||||
|
// Check if the app is in the results
|
||||||
|
if let result = output, result.isEmpty {
|
||||||
diskP = true
|
diskP = true
|
||||||
_ = checkAndRequestAccessibilityAccess(appState: appState)
|
_ = checkAndRequestAccessibilityAccess(appState: appState)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
diskP = false
|
diskP = false
|
||||||
if !skipAlert {
|
if !skipAlert {
|
||||||
appState.alertType = .diskAccess
|
NewWin.show(appState: appState, width: 500, height: 350, newWin: .perm)
|
||||||
appState.showAlert = true
|
// appState.alertType = .diskAccess
|
||||||
|
// appState.showAlert = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// let fileURL = URL(fileURLWithPath: "/Library/Application Support/com.apple.TCC/TCC.db")
|
||||||
|
//
|
||||||
|
// let accessStatus = FileManager.default.isReadableFile(atPath: fileURL.path)
|
||||||
|
// print(accessStatus)
|
||||||
|
// if accessStatus {
|
||||||
|
// diskP = true
|
||||||
|
// _ = checkAndRequestAccessibilityAccess(appState: appState)
|
||||||
|
//
|
||||||
|
// return true
|
||||||
|
// } else {
|
||||||
|
// diskP = false
|
||||||
|
// if !skipAlert {
|
||||||
|
// appState.alertType = .diskAccess
|
||||||
|
// appState.showAlert = true
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -74,6 +137,22 @@ func checkAndRequestAccessibilityAccess(appState: AppState) -> Bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check if appearance is dark mode
|
||||||
|
func isDarkModeEnabled() -> Bool {
|
||||||
|
let process = Process()
|
||||||
|
process.executableURL = URL(fileURLWithPath: "/bin/zsh")
|
||||||
|
process.arguments = ["-c", "defaults read -g AppleInterfaceStyle"]
|
||||||
|
let pipe = Pipe()
|
||||||
|
process.standardOutput = pipe
|
||||||
|
process.standardError = pipe
|
||||||
|
try? process.run()
|
||||||
|
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||||
|
if !data.isEmpty{
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Check if symlink
|
// Check if symlink
|
||||||
@@ -277,35 +356,28 @@ func presentAlert(appState: AppState) -> Alert {
|
|||||||
|
|
||||||
switch appState.alertType {
|
switch appState.alertType {
|
||||||
case .update:
|
case .update:
|
||||||
return Alert(title: Text("Update Available 🥳"), message: Text("You may choose to install the update now, otherwise you may check again later from Settings"), primaryButton: .default(Text("Open")) {
|
return Alert(title: Text("Update Available 🥳"), message: Text("You may choose to install the update now, otherwise you may check again later from Settings"), primaryButton: .default(Text("Install")) {
|
||||||
// downloadUpdate(appState: appState)
|
downloadUpdate(appState: appState)
|
||||||
launchUpdate()
|
// launchUpdate()
|
||||||
appState.alertType = .off
|
appState.alertType = .off
|
||||||
}, secondaryButton: .cancel())
|
}, secondaryButton: .cancel())
|
||||||
case .no_update:
|
case .no_update:
|
||||||
return Alert(title: Text("No Updates 😌"), message: Text("The application is on the latest release available"), primaryButton: .cancel(Text("Okay")), secondaryButton: .default(Text("Force Update")) {
|
return Alert(title: Text("No Updates 😌"), message: Text("Pearcleaner is on the latest release available"), primaryButton: .cancel(Text("Okay")), secondaryButton: .default(Text("Force Update")) {
|
||||||
downloadUpdate(appState: appState)
|
downloadUpdate(appState: appState)
|
||||||
appState.alertType = .off
|
appState.alertType = .off
|
||||||
})
|
})
|
||||||
case .diskAccess:
|
case .diskAccess:
|
||||||
return Alert(title: Text("Permissions"), message: Text("This application requires Full Disk and Accessibility permissions. Drag the app into the Full Disk pane if not visible."), primaryButton: .default(Text("Allow in Settings")) {
|
return Alert(title: Text("Permissions"), message: Text("Pearcleaner requires Full Disk and Accessibility permissions. Drag the app into the Full Disk and Accessibility pane to enable or toggle On if already present."), primaryButton: .default(Text("Allow in Settings")) {
|
||||||
if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") {
|
if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") {
|
||||||
NSWorkspace.shared.open(url)
|
NSWorkspace.shared.open(url)
|
||||||
}
|
}
|
||||||
appState.alertType = .off
|
appState.alertType = .off
|
||||||
}, secondaryButton: .cancel(Text("Later")))
|
}, secondaryButton: .cancel(Text("Later")))
|
||||||
// case .eventsAccess:
|
|
||||||
// return Alert(title: Text("Accessibility Permission"), message: Text("This application requires Accessibility permission"), primaryButton: .default(Text("Allow in Settings")) {
|
|
||||||
// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") {
|
|
||||||
// NSWorkspace.shared.open(url)
|
|
||||||
// }
|
|
||||||
// appState.alertType = .off
|
|
||||||
// }, secondaryButton: .cancel(Text("Later")))
|
|
||||||
case .restartApp:
|
case .restartApp:
|
||||||
return Alert(title: Text("Update Completed!"), message: Text("The application has been updated to the latest version, would you like to restart now?"), primaryButton: .default(Text("Restart")), secondaryButton: .cancel(Text("Later")) {
|
return Alert(title: Text("Update Completed!"), message: Text("The application has been updated to the latest version, would you like to restart now?"), primaryButton: .default(Text("Restart")) {
|
||||||
appState.alertType = .off
|
appState.alertType = .off
|
||||||
relaunchApp(afterDelay: 1)
|
relaunchApp()
|
||||||
})
|
}, secondaryButton: .cancel(Text("Later")))
|
||||||
case .off:
|
case .off:
|
||||||
return Alert(title: Text(""))
|
return Alert(title: Text(""))
|
||||||
}
|
}
|
||||||
@@ -326,7 +398,34 @@ func ensureApplicationSupportFolderExists(appState: AppState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Write Log to File for troubleshooting ---
|
||||||
|
func writeLog(string: String) {
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
let home = fileManager.homeDirectoryForCurrentUser.path
|
||||||
|
let logFilePath = "\(home)/Downloads/log.txt"
|
||||||
|
|
||||||
|
// Check if the log file exists, and create it if it doesn't
|
||||||
|
if !fileManager.fileExists(atPath: logFilePath) {
|
||||||
|
if !fileManager.createFile(atPath: logFilePath, contents: nil, attributes: nil) {
|
||||||
|
print("Failed to create the log file.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if let fileHandle = FileHandle(forWritingAtPath: logFilePath) {
|
||||||
|
let ns = "\(string)\n"
|
||||||
|
fileHandle.seekToEndOfFile()
|
||||||
|
fileHandle.write(ns.data(using: .utf8)!)
|
||||||
|
fileHandle.closeFile()
|
||||||
|
} else {
|
||||||
|
print("Error opening file for appending")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Load Plist file with launchctl ---
|
||||||
func launchctl(load: Bool) {
|
func launchctl(load: Bool) {
|
||||||
let cmd = load ? "load" : "unload"
|
let cmd = load ? "load" : "unload"
|
||||||
if let plistPath = Bundle.main.path(forResource: "com.alienator88.PearcleanerSentinel", ofType: "plist") {
|
if let plistPath = Bundle.main.path(forResource: "com.alienator88.PearcleanerSentinel", ofType: "plist") {
|
||||||
|
|||||||
@@ -15,64 +15,25 @@ struct PearcleanerApp: App {
|
|||||||
@AppStorage("settings.updater.updateTimeframe") private var updateTimeframe: Int = 1
|
@AppStorage("settings.updater.updateTimeframe") private var updateTimeframe: Int = 1
|
||||||
@AppStorage("settings.permissions.disk") private var diskP: Bool = false
|
@AppStorage("settings.permissions.disk") private var diskP: Bool = false
|
||||||
@AppStorage("settings.permissions.events") private var diskE: Bool = false
|
@AppStorage("settings.permissions.events") private var diskE: Bool = false
|
||||||
@AppStorage("displayMode") var displayMode: DisplayMode = .dark
|
@AppStorage("displayMode") var displayMode: DisplayMode = .system
|
||||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
@State private var search = ""
|
@State private var search = ""
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
|
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
|
Group {
|
||||||
|
|
||||||
if !mini {
|
if !mini {
|
||||||
AppListView(search: $search)
|
AppListView(search: $search)
|
||||||
.environmentObject(appState)
|
.frame(minWidth: 800, minHeight: 500)
|
||||||
.frame(minWidth: 1020, minHeight: 700)
|
|
||||||
.preferredColorScheme(displayMode.colorScheme)
|
|
||||||
.alert(isPresented: $appState.showAlert) { presentAlert(appState: appState) }
|
|
||||||
.handlesExternalEvents(preferring: Set(arrayLiteral: "pear"), allowing: Set(arrayLiteral: "*"))
|
|
||||||
.onAppear {
|
|
||||||
NSWindow.allowsAutomaticWindowTabbing = false
|
|
||||||
|
|
||||||
// Get Apps
|
|
||||||
let sortedApps = getSortedApps()
|
|
||||||
appState.sortedApps.userApps = sortedApps.userApps
|
|
||||||
appState.sortedApps.systemApps = sortedApps.systemApps
|
|
||||||
|
|
||||||
Task {
|
|
||||||
|
|
||||||
#if !DEBUG
|
|
||||||
|
|
||||||
// Make sure App Support folder exists in the future if needed for storage
|
|
||||||
ensureApplicationSupportFolderExists(appState: appState)
|
|
||||||
let fileManager = FileManager.default
|
|
||||||
let destinationURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("Pearcleaner").appendingPathComponent("Pearcleaner.zip")
|
|
||||||
try? fileManager.removeItem(at: destinationURL)
|
|
||||||
|
|
||||||
// Check for updates
|
|
||||||
if diskP && diskE {
|
|
||||||
loadGithubReleases(appState: appState)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for disk/accessibility permissions
|
|
||||||
_ = checkAndRequestFullDiskAccess(appState: appState)
|
|
||||||
|
|
||||||
|
|
||||||
// TIMERS ////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Check for app updates every 8 hours or whatever user saved setting. Also refresh autosuggestion list
|
|
||||||
let updateSeconds = updateTimeframe.daysToSeconds
|
|
||||||
_ = Timer.scheduledTimer(withTimeInterval: updateSeconds, repeats: true) { _ in
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
loadGithubReleases(appState: appState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
MiniMode(search: $search)
|
MiniMode(search: $search)
|
||||||
|
.frame(minWidth: 300, minHeight: 300)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
.environmentObject(appState)
|
.environmentObject(appState)
|
||||||
.frame(minWidth: 500, minHeight: 450)
|
|
||||||
.preferredColorScheme(displayMode.colorScheme)
|
.preferredColorScheme(displayMode.colorScheme)
|
||||||
.alert(isPresented: $appState.showAlert) { presentAlert(appState: appState) }
|
.alert(isPresented: $appState.showAlert) { presentAlert(appState: appState) }
|
||||||
.handlesExternalEvents(preferring: Set(arrayLiteral: "pear"), allowing: Set(arrayLiteral: "*"))
|
.handlesExternalEvents(preferring: Set(arrayLiteral: "pear"), allowing: Set(arrayLiteral: "*"))
|
||||||
@@ -90,9 +51,6 @@ struct PearcleanerApp: App {
|
|||||||
|
|
||||||
// Make sure App Support folder exists in the future if needed for storage
|
// Make sure App Support folder exists in the future if needed for storage
|
||||||
ensureApplicationSupportFolderExists(appState: appState)
|
ensureApplicationSupportFolderExists(appState: appState)
|
||||||
let fileManager = FileManager.default
|
|
||||||
let destinationURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("Pearcleaner").appendingPathComponent("Pearcleaner.zip")
|
|
||||||
try? fileManager.removeItem(at: destinationURL)
|
|
||||||
|
|
||||||
// Check for updates
|
// Check for updates
|
||||||
if diskP && diskE {
|
if diskP && diskE {
|
||||||
@@ -116,7 +74,7 @@ struct PearcleanerApp: App {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
.windowStyle(.hiddenTitleBar)
|
.windowStyle(.hiddenTitleBar)
|
||||||
@@ -125,6 +83,7 @@ struct PearcleanerApp: App {
|
|||||||
.commands {
|
.commands {
|
||||||
AppCommands(appState: appState)
|
AppCommands(appState: appState)
|
||||||
CommandGroup(replacing: .newItem, addition: { })
|
CommandGroup(replacing: .newItem, addition: { })
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0xFF",
|
||||||
|
"green" : "0x83",
|
||||||
|
"red" : "0x24"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0xFF",
|
||||||
|
"green" : "0x83",
|
||||||
|
"red" : "0x24"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0xFF",
|
||||||
|
"green" : "0xFF",
|
||||||
|
"red" : "0xFF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "display-p3",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x26",
|
||||||
|
"green" : "0x26",
|
||||||
|
"red" : "0x26"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,16 +7,18 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleTypeName</key>
|
<key>CFBundleTypeName</key>
|
||||||
<string>Application Bundle</string>
|
<string>Application Bundle</string>
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<string>Viewer</string>
|
<string>Viewer</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Default</string>
|
||||||
<key>LSItemContentTypes</key>
|
<key>LSItemContentTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>com.apple.application-bundle</string>
|
<string>com.apple.application-bundle</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
|
<key>CFBundleGetInfoString</key>
|
||||||
|
<string></string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
@@ -30,5 +32,13 @@
|
|||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
|
<key>SUPublicEDKey</key>
|
||||||
|
<string>cD156JWtfwtgFJH2RApyj9Bgc0fCqLBGEf9C9IRMCWE=</string>
|
||||||
|
<key>SUFeedURL</key>
|
||||||
|
<string>https://raw.githubusercontent.com/alienator88/Pearcleaner/main/appcast.xml</string>
|
||||||
|
<key>SUEnableInstallerLauncherService</key>
|
||||||
|
<true/>
|
||||||
|
<key>SUEnableDownloaderService</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ struct AboutSettingsTab: View {
|
|||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text("Made with ❤️ by Alin Lupascu").font(.footnote).padding(.bottom)
|
Text("Made with ❤️ by Alin Lupascu (dev@itsalin.com)").font(.footnote).padding(.bottom)
|
||||||
}
|
}
|
||||||
.padding(20)
|
.padding(20)
|
||||||
.frame(width: 750, height: 325)
|
.frame(width: 750, height: 325)
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ struct GeneralSettingsTab: View {
|
|||||||
@AppStorage("settings.general.glass") private var glass: Bool = true
|
@AppStorage("settings.general.glass") private var glass: Bool = true
|
||||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
@AppStorage("settings.general.dark") var isDark: Bool = true
|
@AppStorage("settings.general.dark") var isDark: Bool = true
|
||||||
@AppStorage("displayMode") var displayMode: DisplayMode = .dark
|
@AppStorage("displayMode") var displayMode: DisplayMode = .system
|
||||||
|
@State private var selectedTheme = "Auto"
|
||||||
|
private let themes = ["Auto", "Dark", "Light"]
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
@@ -38,19 +40,45 @@ struct GeneralSettingsTab: View {
|
|||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
VStack(alignment: .leading, spacing: 5) {
|
VStack(alignment: .leading, spacing: 5) {
|
||||||
Text("Dark Mode").font(.title2)
|
Text("Appearance").font(.title2)
|
||||||
Text("Toggles between dark and light mode")
|
Text("Toggles color mode")
|
||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
.foregroundStyle(.gray)
|
.foregroundStyle(.gray)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
Toggle(isOn: $isDark, label: {
|
|
||||||
})
|
Picker("", selection: $selectedTheme) {
|
||||||
.toggleStyle(.switch)
|
ForEach(themes, id: \.self) { theme in
|
||||||
.onChange(of: isDark) { newValue in
|
Text(theme)
|
||||||
displayMode.colorScheme = newValue ? .dark : .light
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.pickerStyle(SegmentedPickerStyle())
|
||||||
|
// .padding()
|
||||||
|
.onChange(of: selectedTheme) { newTheme in
|
||||||
|
switch newTheme {
|
||||||
|
case "Auto":
|
||||||
|
displayMode.colorScheme = nil
|
||||||
|
if isDarkModeEnabled() {
|
||||||
|
displayMode.colorScheme = .dark
|
||||||
|
} else {
|
||||||
|
displayMode.colorScheme = .light
|
||||||
|
}
|
||||||
|
case "Dark":
|
||||||
|
displayMode.colorScheme = .dark
|
||||||
|
case "Light":
|
||||||
|
displayMode.colorScheme = .light
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Toggle(isOn: $isDark, label: {
|
||||||
|
// })
|
||||||
|
// .toggleStyle(.switch)
|
||||||
|
// .onChange(of: isDark) { newValue in
|
||||||
|
// displayMode.colorScheme = newValue ? .dark : .light
|
||||||
|
// }
|
||||||
|
}
|
||||||
.padding()
|
.padding()
|
||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: 8)
|
RoundedRectangle(cornerRadius: 8)
|
||||||
@@ -69,7 +97,16 @@ struct GeneralSettingsTab: View {
|
|||||||
})
|
})
|
||||||
.toggleStyle(.switch)
|
.toggleStyle(.switch)
|
||||||
.onChange(of: mini) { newVal in
|
.onChange(of: mini) { newVal in
|
||||||
resizeWindow()
|
resizeWindow(width: 300, height: 300)
|
||||||
|
if mini {
|
||||||
|
appState.currentView = .apps
|
||||||
|
} else {
|
||||||
|
if appState.appInfo.appName.isEmpty {
|
||||||
|
appState.currentView = .empty
|
||||||
|
} else {
|
||||||
|
appState.currentView = .files
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
|
|||||||
@@ -39,13 +39,17 @@ struct SentinelSettingsTab: View {
|
|||||||
RoundedRectangle(cornerRadius: 8)
|
RoundedRectangle(cornerRadius: 8)
|
||||||
.fill(Color("mode").opacity(0.05))
|
.fill(Color("mode").opacity(0.05))
|
||||||
)
|
)
|
||||||
|
Text("If you uninstall Pearcleaner, make sure to disable Sentinel first").font(.footnote)
|
||||||
|
.foregroundStyle(Color.red)
|
||||||
|
.padding()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
.padding(20)
|
.padding(20)
|
||||||
.frame(width: 450, height: 100)
|
.frame(width: 450, height: 130)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ struct UpdateSettingsTab: View {
|
|||||||
func loadGithubReleases(appState: AppState, manual: Bool = false) {
|
func loadGithubReleases(appState: AppState, manual: Bool = false) {
|
||||||
let url = URL(string: "https://api.github.com/repos/alienator88/Pearcleaner/releases")!
|
let url = URL(string: "https://api.github.com/repos/alienator88/Pearcleaner/releases")!
|
||||||
let request = URLRequest(url: url)
|
let request = URLRequest(url: url)
|
||||||
|
// Set the token for a private repo
|
||||||
// request.setValue("token \(ghToken)", forHTTPHeaderField: "Authorization")
|
// request.setValue("token \(ghToken)", forHTTPHeaderField: "Authorization")
|
||||||
URLSession.shared.dataTask(with: request) { data, response, error in
|
URLSession.shared.dataTask(with: request) { data, response, error in
|
||||||
if let data = data {
|
if let data = data {
|
||||||
@@ -114,22 +115,25 @@ func checkForUpdate(appState: AppState, manual: Bool = false) {
|
|||||||
guard let latestRelease = appState.releases.first else { return }
|
guard let latestRelease = appState.releases.first else { return }
|
||||||
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
||||||
if latestRelease.tag_name > currentVersion ?? "" {
|
if latestRelease.tag_name > currentVersion ?? "" {
|
||||||
appState.alertType = .update
|
NewWin.show(appState: appState, width: 500, height: 400, newWin: .update)
|
||||||
appState.showAlert = true
|
// appState.alertType = .update
|
||||||
|
// appState.showAlert = true
|
||||||
} else {
|
} else {
|
||||||
if manual {
|
if manual {
|
||||||
appState.alertType = .no_update
|
NewWin.show(appState: appState, width: 500, height: 300, newWin: .no_update)
|
||||||
appState.showAlert = true
|
// appState.alertType = .no_update
|
||||||
|
// appState.showAlert = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func launchUpdate() {
|
|
||||||
NSWorkspace.shared.open(URL(string: "https://github.com/alienator88/Pearcleaner/releases")!)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func downloadUpdate(appState: AppState) {
|
func downloadUpdate(appState: AppState) {
|
||||||
|
updateOnMain {
|
||||||
|
appState.progressBar.0 = "Getting update file links ready"
|
||||||
|
appState.progressBar.1 = 0.1
|
||||||
|
}
|
||||||
|
|
||||||
guard let latestRelease = appState.releases.first else { return }
|
guard let latestRelease = appState.releases.first else { return }
|
||||||
guard let asset = latestRelease.assets.first else { return }
|
guard let asset = latestRelease.assets.first else { return }
|
||||||
guard let url = URL(string: asset.url) else { return }
|
guard let url = URL(string: asset.url) else { return }
|
||||||
@@ -138,6 +142,11 @@ func downloadUpdate(appState: AppState) {
|
|||||||
request.setValue("application/octet-stream", forHTTPHeaderField: "Accept")
|
request.setValue("application/octet-stream", forHTTPHeaderField: "Accept")
|
||||||
|
|
||||||
let downloadTask = URLSession.shared.downloadTask(with: request) { localURL, urlResponse, error in
|
let downloadTask = URLSession.shared.downloadTask(with: request) { localURL, urlResponse, error in
|
||||||
|
updateOnMain {
|
||||||
|
appState.progressBar.0 = "Downloading update file"
|
||||||
|
appState.progressBar.1 = 0.2
|
||||||
|
}
|
||||||
|
|
||||||
guard let localURL = localURL else { return }
|
guard let localURL = localURL else { return }
|
||||||
|
|
||||||
let fileManager = FileManager.default
|
let fileManager = FileManager.default
|
||||||
@@ -147,15 +156,21 @@ func downloadUpdate(appState: AppState) {
|
|||||||
if fileManager.fileExists(atPath: destinationURL.path) {
|
if fileManager.fileExists(atPath: destinationURL.path) {
|
||||||
try? fileManager.removeItem(at: destinationURL)
|
try? fileManager.removeItem(at: destinationURL)
|
||||||
}
|
}
|
||||||
|
updateOnMain {
|
||||||
|
appState.progressBar.0 = "Moving update file to Application Support"
|
||||||
|
appState.progressBar.1 = 0.4
|
||||||
|
}
|
||||||
|
|
||||||
try fileManager.moveItem(at: localURL, to: destinationURL)
|
try fileManager.moveItem(at: localURL, to: destinationURL)
|
||||||
|
|
||||||
UnzipAndReplace(DownloadedFileURL: destinationURL.path, appState: appState) {
|
UnzipAndReplace(DownloadedFileURL: destinationURL.path, appState: appState)
|
||||||
|
|
||||||
updateOnMain {
|
updateOnMain {
|
||||||
// Restart App
|
appState.progressBar.0 = "Done, please restart!"
|
||||||
relaunchApp(afterDelay: 1)
|
appState.progressBar.1 = 1.0
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
print("Error moving downloaded file: \(error.localizedDescription)")
|
print("Error moving downloaded file: \(error.localizedDescription)")
|
||||||
}
|
}
|
||||||
@@ -164,28 +179,61 @@ func downloadUpdate(appState: AppState) {
|
|||||||
downloadTask.resume()
|
downloadTask.resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnzipAndReplace(DownloadedFileURL fileURL: String, appState: AppState, completion: @escaping () -> Void = {}) {
|
func UnzipAndReplace(DownloadedFileURL fileURL: String, appState: AppState) {
|
||||||
let appDirectory = Bundle.main.bundleURL.deletingLastPathComponent().path
|
let appDirectory = Bundle.main.bundleURL.deletingLastPathComponent().path
|
||||||
let appBundle = Bundle.main.bundleURL.path
|
let appBundle = Bundle.main.bundleURL.path
|
||||||
let fileManager = FileManager.default
|
let fileManager = FileManager.default
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
updateOnMain {
|
||||||
|
appState.progressBar.0 = "Deleting existing application"
|
||||||
|
appState.progressBar.1 = 0.5
|
||||||
|
}
|
||||||
|
|
||||||
// Remove the old version of your app
|
// Remove the old version of your app
|
||||||
try fileManager.removeItem(atPath: appBundle)
|
try fileManager.removeItem(atPath: appBundle)
|
||||||
|
|
||||||
|
updateOnMain {
|
||||||
|
appState.progressBar.0 = "Unziping new update file to original Pearcleaner location"
|
||||||
|
appState.progressBar.1 = 0.6
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Unzip the downloaded update file to your app's bundle path
|
// Unzip the downloaded update file to your app's bundle path
|
||||||
let process = Process()
|
let process = Process()
|
||||||
process.executableURL = URL(fileURLWithPath: "/usr/bin/unzip")
|
// process.executableURL = URL(fileURLWithPath: "/usr/bin/unzip")
|
||||||
process.arguments = [fileURL, "-d", appDirectory]
|
// process.arguments = [fileURL, "-d", appDirectory]
|
||||||
|
process.executableURL = URL(fileURLWithPath: "/usr/bin/ditto")
|
||||||
|
process.arguments = ["-xk", fileURL, appDirectory]
|
||||||
|
// let pipe = Pipe()
|
||||||
|
process.standardOutput = FileHandle.nullDevice
|
||||||
|
process.standardError = FileHandle.nullDevice
|
||||||
|
|
||||||
try process.run()
|
try process.run()
|
||||||
process.waitUntilExit()
|
process.waitUntilExit()
|
||||||
|
|
||||||
|
// let outputData = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||||
|
// let output = String(decoding: outputData, as: UTF8.self)
|
||||||
|
//
|
||||||
|
// writeLog(string: output)
|
||||||
|
|
||||||
|
updateOnMain {
|
||||||
|
appState.progressBar.0 = "Deleting update file"
|
||||||
|
appState.progressBar.1 = 0.8
|
||||||
|
}
|
||||||
|
|
||||||
// After unzipping, remove the update file
|
// After unzipping, remove the update file
|
||||||
try fileManager.removeItem(atPath: fileURL)
|
try fileManager.removeItem(atPath: fileURL)
|
||||||
|
|
||||||
|
// Show Restart dialog
|
||||||
|
// updateOnMain {
|
||||||
|
// appState.alertType = .restartApp
|
||||||
|
// appState.showAlert = true
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
print("Error updating the app: \(error)")
|
print("Error updating the app: \(error)")
|
||||||
}
|
}
|
||||||
|
|
||||||
completion()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,12 @@ import SwiftUI
|
|||||||
|
|
||||||
struct AppListItems: View {
|
struct AppListItems: View {
|
||||||
@EnvironmentObject var appState: AppState
|
@EnvironmentObject var appState: AppState
|
||||||
|
// @Binding var reload: Bool
|
||||||
|
// @Binding var search: String
|
||||||
@State private var isHovered = false
|
@State private var isHovered = false
|
||||||
@Environment(\.colorScheme) var colorScheme
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||||
|
|
||||||
let appInfo: AppInfo
|
let appInfo: AppInfo
|
||||||
var isSelected: Bool {
|
var isSelected: Bool {
|
||||||
appState.appInfo == appInfo
|
appState.appInfo == appInfo
|
||||||
@@ -39,6 +43,16 @@ struct AppListItems: View {
|
|||||||
.truncationMode(.tail)
|
.truncationMode(.tail)
|
||||||
.padding(.horizontal, 5)
|
.padding(.horizontal, 5)
|
||||||
}
|
}
|
||||||
|
if appInfo.webApp {
|
||||||
|
Text("web")
|
||||||
|
.font(.footnote)
|
||||||
|
.foregroundStyle(Color("mode").opacity(0.5))
|
||||||
|
.frame(minWidth: 30, minHeight: 15)
|
||||||
|
.padding(2)
|
||||||
|
.background(Color("mode").opacity(0.1))
|
||||||
|
.clipShape(.capsule)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
Text(appInfo.appVersion)
|
Text(appInfo.appVersion)
|
||||||
@@ -82,9 +96,26 @@ struct AppListItems: View {
|
|||||||
appState.appInfo = appInfo
|
appState.appInfo = appInfo
|
||||||
findPathsForApp(appState: appState, appInfo: appState.appInfo)
|
findPathsForApp(appState: appState, appInfo: appState.appInfo)
|
||||||
withAnimation(Animation.easeIn(duration: 0.4)) {
|
withAnimation(Animation.easeIn(duration: 0.4)) {
|
||||||
|
if mini {
|
||||||
|
appState.showPopover.toggle()
|
||||||
|
} else {
|
||||||
appState.currentView = .files
|
appState.currentView = .files
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// .popover(isPresented: $appState.showPopover, arrowEdge: .trailing) {
|
||||||
|
// VStack {
|
||||||
|
// FilesView()
|
||||||
|
// .id(appState.appInfo.id)
|
||||||
|
// }
|
||||||
|
// .background(
|
||||||
|
// Rectangle()
|
||||||
|
// .background(colorScheme == .dark ? .black.opacity(0.8) : .white)
|
||||||
|
// .padding(-80)
|
||||||
|
// )
|
||||||
|
// .frame(minWidth: 500, minHeight: 500)
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,14 +47,14 @@ struct AppListView: View {
|
|||||||
ProgressView("Refreshing applications")
|
ProgressView("Refreshing applications")
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.frame(width: 300)
|
.frame(width: 250)
|
||||||
.padding(.vertical)
|
.padding(.vertical)
|
||||||
} else {
|
} else {
|
||||||
VStack(alignment: .center) {
|
VStack(alignment: .center) {
|
||||||
|
|
||||||
VStack(alignment: .center, spacing: 20) {
|
VStack(alignment: .center, spacing: 20) {
|
||||||
HStack {
|
HStack {
|
||||||
SearchBar(search: $search)
|
SearchBar(search: $search, reload: $reload)
|
||||||
|
|
||||||
Button("") {
|
Button("") {
|
||||||
withAnimation(.easeInOut(duration: 0.5)) {
|
withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
@@ -89,7 +89,7 @@ struct AppListView: View {
|
|||||||
Divider().padding(.horizontal, 5)
|
Divider().padding(.horizontal, 5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.bottom)
|
// .padding(.bottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ struct AppListView: View {
|
|||||||
.scrollIndicators(.never)
|
.scrollIndicators(.never)
|
||||||
|
|
||||||
}
|
}
|
||||||
.frame(width: 300)
|
.frame(width: 250)
|
||||||
.padding(.vertical)
|
.padding(.vertical)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,11 +200,12 @@ struct AppDetailsEmptyView: View {
|
|||||||
|
|
||||||
struct SearchBar: View {
|
struct SearchBar: View {
|
||||||
@Binding var search: String
|
@Binding var search: String
|
||||||
|
@Binding var reload: Bool
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
TextField("Search", text: $search)
|
TextField("Search", text: $search)
|
||||||
.textFieldStyle(SimpleSearchStyle(icon: Image(systemName: "magnifyingglass"),trash: true, text: $search))
|
.textFieldStyle(SimpleSearchStyle(icon: Image(systemName: "magnifyingglass"),trash: true, reload: $reload, text: $search))
|
||||||
}
|
}
|
||||||
.frame(height: 30)
|
.frame(height: 30)
|
||||||
}
|
}
|
||||||
@@ -220,11 +221,13 @@ struct Header: View {
|
|||||||
Text(title).opacity(0.5)
|
Text(title).opacity(0.5)
|
||||||
Spacer()
|
Spacer()
|
||||||
Text("\(count)")
|
Text("\(count)")
|
||||||
.frame(minWidth: 30, minHeight: 15)
|
.font(.system(size: 10))
|
||||||
|
.frame(minWidth: count > 99 ? 30 : 20, minHeight: 15)
|
||||||
.padding(2)
|
.padding(2)
|
||||||
.background(Color("mode").opacity(0.1))
|
.background(Color("mode").opacity(0.1))
|
||||||
.clipShape(.capsule)
|
.clipShape(.capsule)
|
||||||
// .clipShape(RoundedRectangle(cornerRadius: 6, style: .continuous))
|
// .clipShape(RoundedRectangle(cornerRadius: 6, style: .continuous))
|
||||||
}
|
}
|
||||||
|
.padding(4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
////
|
|
||||||
//// ContentView.swift
|
|
||||||
//// Pearcleaner
|
|
||||||
////
|
|
||||||
//// Created by Alin Lupascu on 10/31/23.
|
|
||||||
////
|
|
||||||
//
|
|
||||||
//import SwiftUI
|
|
||||||
//
|
|
||||||
//struct ContentView: View {
|
|
||||||
// @EnvironmentObject var appState: AppState
|
|
||||||
// @State private var search = ""
|
|
||||||
//
|
|
||||||
// var body: some View {
|
|
||||||
//
|
|
||||||
// VStack(alignment: .leading) {
|
|
||||||
//// AppListNav(search: $search)
|
|
||||||
// AppListH(search: $search)
|
|
||||||
//// HStack {
|
|
||||||
//// Image(nsImage: NSApp.applicationIconImage)
|
|
||||||
//// .imageScale(.large)
|
|
||||||
////
|
|
||||||
//// Text("Pearcleaner")
|
|
||||||
//// .font(.custom("MontserratAlt1-Light", size: 30, relativeTo: .body))
|
|
||||||
//// }
|
|
||||||
//// .padding()
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//// VStack() {
|
|
||||||
////// AppListView(search: $search)
|
|
||||||
////// AppListNav(search: $search)
|
|
||||||
//// }
|
|
||||||
// }
|
|
||||||
//// .sheet(isPresented: $appState.showFiles, content: {
|
|
||||||
//// FilesView()
|
|
||||||
//// })
|
|
||||||
// .alert(isPresented: $appState.showAlert) { presentAlert(appState: appState) }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
@@ -16,7 +16,7 @@ struct DropTarget: View, DropDelegate {
|
|||||||
@State private var shouldFlash = false
|
@State private var shouldFlash = false
|
||||||
private var types: [UTType] = [.fileURL]
|
private var types: [UTType] = [.fileURL]
|
||||||
@Environment(\.colorScheme) var colorScheme
|
@Environment(\.colorScheme) var colorScheme
|
||||||
@State private var phase: CGFloat = 0
|
@State private var phase: CGFloat = 4
|
||||||
|
|
||||||
public init(appState: AppState) {
|
public init(appState: AppState) {
|
||||||
self.appState = appState
|
self.appState = appState
|
||||||
@@ -39,7 +39,9 @@ struct DropTarget: View, DropDelegate {
|
|||||||
Image(mini ? "pear" : "logo")
|
Image(mini ? "pear" : "logo")
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.frame(width: mini ? 104 : 500)
|
.frame(minWidth: mini ? 104 : 300)
|
||||||
|
.frame(maxWidth: mini ? 104 : 500)
|
||||||
|
.padding(mini ? 20 : 10)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,17 +54,17 @@ struct DropTarget: View, DropDelegate {
|
|||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
.background {
|
.background {
|
||||||
RoundedRectangle(cornerRadius: 8)
|
RoundedRectangle(cornerRadius: 8)
|
||||||
.strokeBorder(LinearGradient(gradient: Gradient(colors: [.pink, .orange]), startPoint: .leading, endPoint: .trailing), style: StrokeStyle(lineWidth: 3, dash: [10], dashPhase: phase))
|
.strokeBorder(LinearGradient(gradient: Gradient(colors: [.pink, .orange]), startPoint: .leading, endPoint: .trailing), style: StrokeStyle(lineWidth: 3, dash: [11.0], dashPhase: phase))
|
||||||
.onAppear {
|
.onAppear {
|
||||||
withAnimation(.linear.speed(0.5).repeat(while: ants, autoreverses: false)) {
|
withAnimation(.linear.speed(0.5).repeat(while: ants, autoreverses: false)) {
|
||||||
if ants {
|
if ants {
|
||||||
phase -= 20
|
phase -= 22
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: ants) { newValue in
|
.onChange(of: ants) { newValue in
|
||||||
withAnimation(.linear.speed(0.5).repeat(while: newValue, autoreverses: false)) {
|
withAnimation(.linear.speed(0.5).repeat(while: newValue, autoreverses: false)) {
|
||||||
phase -= 20
|
phase -= 22
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ struct FilesView: View {
|
|||||||
ProgressView("Finding application files..")
|
ProgressView("Finding application files..")
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
} else {
|
} else {
|
||||||
VStack() {
|
VStack() {
|
||||||
@@ -45,19 +46,19 @@ struct FilesView: View {
|
|||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
VStack(alignment: .leading, spacing: 5){
|
VStack(alignment: .leading, spacing: 5){
|
||||||
HStack {
|
HStack {
|
||||||
Text("\(appState.appInfo.appName)").font(mini ? .title2 :.title).fontWeight(.bold)
|
Text("\(appState.appInfo.appName)").font(.title).fontWeight(.bold)
|
||||||
// .foregroundStyle(Color("AccentColor"))
|
// .foregroundStyle(Color("AccentColor"))
|
||||||
Text("•").foregroundStyle(Color("AccentColor"))
|
Text("•").foregroundStyle(Color("AccentColor"))
|
||||||
Text("\(appState.appInfo.appVersion)").font(.title3)
|
Text("\(appState.appInfo.appVersion)").font(.title3)
|
||||||
// .foregroundStyle(.gray.opacity(0.8))
|
// .foregroundStyle(.gray.opacity(0.8))
|
||||||
}
|
}
|
||||||
Text("\(appState.appInfo.bundleIdentifier)").font(mini ? .footnote :.title3)
|
Text("\(appState.appInfo.bundleIdentifier)").font(.title3)
|
||||||
.foregroundStyle((.gray.opacity(0.8)))
|
.foregroundStyle((.gray.opacity(0.8)))
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text("\(appSize)").font(mini ? .title2 :.title).fontWeight(.bold)
|
Text("\(appSize)").font(.title).fontWeight(.bold)
|
||||||
// .foregroundStyle(Color("AccentColor"))
|
// .foregroundStyle(Color("AccentColor"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +75,19 @@ struct FilesView: View {
|
|||||||
// .foregroundStyle(Color("AccentColor").opacity(0.7))
|
// .foregroundStyle(Color("AccentColor").opacity(0.7))
|
||||||
.underline()
|
.underline()
|
||||||
}
|
}
|
||||||
|
if appState.appInfo.webApp {
|
||||||
|
HStack {
|
||||||
|
Text("web")
|
||||||
|
.font(.footnote)
|
||||||
|
.foregroundStyle(Color("mode").opacity(0.5))
|
||||||
|
.frame(minWidth: 30, minHeight: 15)
|
||||||
|
.padding(2)
|
||||||
|
.background(Color("mode").opacity(0.1))
|
||||||
|
.clipShape(.capsule)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
if appState.appInfo.appName.count < 5 {
|
if appState.appInfo.appName.count < 5 {
|
||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
Button(""){
|
Button(""){
|
||||||
@@ -103,7 +117,7 @@ struct FilesView: View {
|
|||||||
}
|
}
|
||||||
.padding(20)
|
.padding(20)
|
||||||
}
|
}
|
||||||
.padding([.horizontal, .bottom])
|
// .padding(.horizontal)
|
||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: 8)
|
RoundedRectangle(cornerRadius: 8)
|
||||||
// .strokeBorder(Color("AccentColor"), lineWidth: 0.5)
|
// .strokeBorder(Color("AccentColor"), lineWidth: 0.5)
|
||||||
@@ -142,16 +156,6 @@ struct FilesView: View {
|
|||||||
HStack() {
|
HStack() {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
// if appState.trashLaunch {
|
|
||||||
// Button("Close") {
|
|
||||||
// Task {
|
|
||||||
// NSApp.terminate(nil)
|
|
||||||
// exit(0)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .buttonStyle(WindowActionButton(action: .cancel))
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
Button("Uninstall") {
|
Button("Uninstall") {
|
||||||
Task {
|
Task {
|
||||||
@@ -182,7 +186,7 @@ struct FilesView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
// .frame(minWidth: 700)
|
// .frame(minWidth: 500, minHeight: 500)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
Task {
|
Task {
|
||||||
calculateFileDetails()
|
calculateFileDetails()
|
||||||
@@ -302,144 +306,3 @@ struct FileDetailsItem: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func writeLog(string: String) {
|
|
||||||
let fileManager = FileManager.default
|
|
||||||
let home = fileManager.homeDirectoryForCurrentUser.path
|
|
||||||
let logFilePath = "\(home)/Downloads/log.txt"
|
|
||||||
|
|
||||||
// Check if the log file exists, and create it if it doesn't
|
|
||||||
if !fileManager.fileExists(atPath: logFilePath) {
|
|
||||||
if !fileManager.createFile(atPath: logFilePath, contents: nil, attributes: nil) {
|
|
||||||
print("Failed to create the log file.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
if let fileHandle = FileHandle(forWritingAtPath: logFilePath) {
|
|
||||||
let ns = "\(string)\n"
|
|
||||||
fileHandle.seekToEndOfFile()
|
|
||||||
fileHandle.write(ns.data(using: .utf8)!)
|
|
||||||
fileHandle.closeFile()
|
|
||||||
} else {
|
|
||||||
print("Error opening file for appending")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//struct FilesView: View {
|
|
||||||
// @EnvironmentObject var appState: AppState
|
|
||||||
// @State private var appSize: String = ""
|
|
||||||
// @State private var showDetails: Bool = false
|
|
||||||
// @State private var itemDetails: [(size: String, icon: Image?)] = []
|
|
||||||
//
|
|
||||||
// var body: some View {
|
|
||||||
// VStack(alignment: .center) {
|
|
||||||
// if !self.showDetails {
|
|
||||||
// ProgressView("Finding application files..")
|
|
||||||
// } else {
|
|
||||||
// VStack() {
|
|
||||||
// HStack() {
|
|
||||||
// Text("\(appState.paths.count > 1 ? "\(appState.paths.count) files found" : "\(appState.paths.count) file found")")
|
|
||||||
// Text("•")
|
|
||||||
// Text("\(appSize)")
|
|
||||||
// }
|
|
||||||
// .padding()
|
|
||||||
//
|
|
||||||
// if appState.appInfo.appName.count < 5 {
|
|
||||||
// Text("Short or common word app name, there might be unrelated files found")
|
|
||||||
// .font(.footnote)
|
|
||||||
// .foregroundStyle(.red)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// ScrollView {
|
|
||||||
// ForEach(Array(zip(appState.paths, itemDetails)), id: \.0) { path, details in
|
|
||||||
// FileDetailsItem(size: details.size, icon: details.icon, path: path)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .frame(width: 500)
|
|
||||||
//
|
|
||||||
// Button("Remove") {
|
|
||||||
// Task {
|
|
||||||
// updateOnMain {
|
|
||||||
// appState.appInfo = AppInfo.empty
|
|
||||||
// }
|
|
||||||
// let selectedItemsArray = Array(appState.selectedItems)
|
|
||||||
// killApp(appId: appState.appInfo.bundleIdentifier) {
|
|
||||||
// moveFilesToTrash(at: selectedItemsArray) {
|
|
||||||
// refreshAppList(appState.appInfo)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .disabled(appState.selectedItems.isEmpty)
|
|
||||||
// }
|
|
||||||
// .padding()
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// .frame(minWidth: 500)
|
|
||||||
// .onAppear {
|
|
||||||
// Task {
|
|
||||||
// // appState.appInfo = appInfo
|
|
||||||
// // findPathsForApp(appState: appState, appInfo: appState.appInfo)
|
|
||||||
// calculateFileDetails()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// func calculateFileDetails() {
|
|
||||||
// if appState.paths.count != 0 {
|
|
||||||
// itemDetails = Array(repeating: (size: "", icon: nil), count: appState.paths.count)
|
|
||||||
//
|
|
||||||
// Task {
|
|
||||||
// for (index, path) in appState.paths.enumerated() {
|
|
||||||
// var size = ""
|
|
||||||
// var icon: Image? = nil
|
|
||||||
//
|
|
||||||
// if let appSize = totalSizeOnDisk(for: path) {
|
|
||||||
// size = "\(appSize)"
|
|
||||||
// } else {
|
|
||||||
// print("Error calculating the total size on disk for item \(index).")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if let folderIcon = getIconForFileOrFolder(atPath: path) {
|
|
||||||
// icon = folderIcon
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// itemDetails[index] = (size: size, icon: icon)
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if let appSize = totalSizeOnDisk(for: appState.paths) {
|
|
||||||
// self.appSize = "\(appSize)"
|
|
||||||
// // updateOnMain {
|
|
||||||
// // appState.doneLoading = true
|
|
||||||
// // }
|
|
||||||
// self.showDetails = true
|
|
||||||
// // appState.doneLoading = true
|
|
||||||
// } else {
|
|
||||||
// print("Error calculating the total size on disk.")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
||||||
// calculateFileDetails()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func refreshAppList(_ appInfo: AppInfo) {
|
|
||||||
// let sortedApps = getSortedApps()
|
|
||||||
// appState.sortedApps.userApps = []
|
|
||||||
// appState.sortedApps.systemApps = []
|
|
||||||
// appState.sortedApps.userApps = sortedApps.userApps
|
|
||||||
// appState.sortedApps.systemApps = sortedApps.systemApps
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ struct MiniMode: View {
|
|||||||
@State private var showSys: Bool = true
|
@State private var showSys: Bool = true
|
||||||
@State private var showUsr: Bool = true
|
@State private var showUsr: Bool = true
|
||||||
@State private var reload: Bool = false
|
@State private var reload: Bool = false
|
||||||
|
@AppStorage("settings.general.glass") private var glass: Bool = false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ struct MiniMode: View {
|
|||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
if appState.currentView == .empty {
|
if appState.currentView == .empty {
|
||||||
TopBarMini(reload: $reload, search: $search)
|
TopBarMini(reload: $reload, search: $search)
|
||||||
AppDetailsEmptyView()
|
MiniEmptyView()
|
||||||
} else if appState.currentView == .files {
|
} else if appState.currentView == .files {
|
||||||
TopBarMini(reload: $reload, search: $search)
|
TopBarMini(reload: $reload, search: $search)
|
||||||
FilesView()
|
FilesView()
|
||||||
@@ -39,8 +40,11 @@ struct MiniMode: View {
|
|||||||
// .padding(.leading, appState.sidebar ? 0 : 10)
|
// .padding(.leading, appState.sidebar ? 0 : 10)
|
||||||
.transition(.move(edge: .leading))
|
.transition(.move(edge: .leading))
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
.frame(minWidth: 300, minHeight: 300)
|
||||||
.edgesIgnoringSafeArea(.all)
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
.background(glass ? GlassEffect(material: .sidebar, blendingMode: .behindWindow).edgesIgnoringSafeArea(.all) : nil)
|
||||||
.onOpenURL(perform: { url in
|
.onOpenURL(perform: { url in
|
||||||
let deeplinkManager = DeeplinkManager()
|
let deeplinkManager = DeeplinkManager()
|
||||||
deeplinkManager.manage(url: url, appState: appState)
|
deeplinkManager.manage(url: url, appState: appState)
|
||||||
@@ -70,6 +74,11 @@ struct MiniEmptyView: View {
|
|||||||
DropTarget(appState: appState)
|
DropTarget(appState: appState)
|
||||||
.frame(maxWidth: mini ? 300 : 500)
|
.frame(maxWidth: mini ? 300 : 500)
|
||||||
|
|
||||||
|
Text("Drop an app to begin")
|
||||||
|
.font(.title3)
|
||||||
|
.padding(.bottom, 25)
|
||||||
|
.opacity(0.5)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if appState.isReminderVisible {
|
if appState.isReminderVisible {
|
||||||
@@ -87,11 +96,9 @@ struct MiniEmptyView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
||||||
Text("Drop an app above or select one from the list to begin")
|
|
||||||
.font(.title3)
|
|
||||||
.padding(.bottom, 25)
|
|
||||||
.opacity(0.5)
|
|
||||||
}
|
}
|
||||||
|
// .padding()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,14 +147,6 @@ struct MiniAppView: View {
|
|||||||
} else {
|
} else {
|
||||||
VStack(alignment: .center) {
|
VStack(alignment: .center) {
|
||||||
|
|
||||||
// VStack(alignment: .center, spacing: 20) {
|
|
||||||
// HStack {
|
|
||||||
// SearchBar(search: $search)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .padding(.horizontal, 5)
|
|
||||||
// .padding(.bottom)
|
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
@@ -161,7 +160,6 @@ struct MiniAppView: View {
|
|||||||
Divider().padding(.horizontal, 5)
|
Divider().padding(.horizontal, 5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.bottom)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -185,13 +183,27 @@ struct MiniAppView: View {
|
|||||||
.scrollIndicators(.never)
|
.scrollIndicators(.never)
|
||||||
|
|
||||||
}
|
}
|
||||||
.padding(.vertical)
|
// .padding(.vertical)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.transition(.move(edge: .leading))
|
.transition(.move(edge: .leading))
|
||||||
|
.popover(isPresented: $appState.showPopover, arrowEdge: .trailing) {
|
||||||
|
VStack {
|
||||||
|
FilesView()
|
||||||
|
.id(appState.appInfo.id)
|
||||||
|
}
|
||||||
|
.background(
|
||||||
|
Rectangle()
|
||||||
|
.fill(Color("pop"))
|
||||||
|
// .background(Color("pop"))
|
||||||
|
.padding(-80)
|
||||||
|
)
|
||||||
|
.frame(minWidth: 600, minHeight: 500)
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,38 @@ struct TopBarMini: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(alignment: .center, spacing: 5) {
|
HStack(alignment: .center, spacing: 5) {
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
|
if appState.currentView == .apps {
|
||||||
|
// HStack {
|
||||||
|
// Spacer()
|
||||||
|
|
||||||
|
SearchBarMini(search: $search, reload: $reload)
|
||||||
|
.frame(width: 180)
|
||||||
|
// .padding(.top, 5)
|
||||||
|
|
||||||
|
// Spacer()
|
||||||
|
|
||||||
|
// Button("") {
|
||||||
|
// withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
|
// // Refresh Apps list
|
||||||
|
// reload.toggle()
|
||||||
|
// let sortedApps = getSortedApps()
|
||||||
|
// DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||||
|
// appState.sortedApps.userApps = sortedApps.userApps
|
||||||
|
// appState.sortedApps.systemApps = sortedApps.systemApps
|
||||||
|
// reload.toggle()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// .buttonStyle(SimpleButtonStyle(icon: "arrow.triangle.2.circlepath", help: "Refresh app list", color: Color("mode")))
|
||||||
|
// .padding(.leading, 5)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
if appState.currentView == .apps {
|
if appState.currentView == .apps {
|
||||||
HStack(alignment: .center, spacing: 0) {
|
|
||||||
Button("") {
|
Button("") {
|
||||||
withAnimation(.easeInOut(duration: 0.5)) {
|
withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
appState.currentView = .empty
|
appState.currentView = .empty
|
||||||
@@ -28,10 +57,6 @@ struct TopBarMini: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.buttonStyle(SimpleButtonStyle(icon: "arrow.down.app", help: "Drop", color: Color("mode")))
|
.buttonStyle(SimpleButtonStyle(icon: "arrow.down.app", help: "Drop", color: Color("mode")))
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if appState.currentView == .empty {
|
} else if appState.currentView == .empty {
|
||||||
Button("") {
|
Button("") {
|
||||||
withAnimation(.easeInOut(duration: 0.5)) {
|
withAnimation(.easeInOut(duration: 0.5)) {
|
||||||
@@ -55,56 +80,22 @@ struct TopBarMini: View {
|
|||||||
.buttonStyle(SimpleButtonStyle(icon: "list.triangle", help: "Apps", color: Color("mode")))
|
.buttonStyle(SimpleButtonStyle(icon: "list.triangle", help: "Apps", color: Color("mode")))
|
||||||
}
|
}
|
||||||
|
|
||||||
if appState.currentView == .apps {
|
|
||||||
HStack {
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
SearchBarMini(search: $search)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Button("") {
|
|
||||||
withAnimation(.easeInOut(duration: 0.5)) {
|
|
||||||
// Refresh Apps list
|
|
||||||
reload.toggle()
|
|
||||||
let sortedApps = getSortedApps()
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
|
||||||
appState.sortedApps.userApps = sortedApps.userApps
|
|
||||||
appState.sortedApps.systemApps = sortedApps.systemApps
|
|
||||||
reload.toggle()
|
|
||||||
}
|
}
|
||||||
|
.padding(.horizontal, 10)
|
||||||
}
|
.padding(.top, 10)
|
||||||
}
|
.padding(.bottom, 15)
|
||||||
.buttonStyle(SimpleButtonStyle(icon: "arrow.triangle.2.circlepath", help: "Refresh app list", color: Color("mode")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
// if sentinel {
|
|
||||||
// Button("") {
|
|
||||||
// //
|
|
||||||
// }
|
|
||||||
// .buttonStyle(SimpleButtonStyle(icon: "lock.shield", help: "Sentinel enabled", color: .green, shield: true))
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
|
||||||
.edgesIgnoringSafeArea(.all)
|
|
||||||
.padding(.horizontal)
|
|
||||||
.padding(.top, 30)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct SearchBarMini: View {
|
struct SearchBarMini: View {
|
||||||
@Binding var search: String
|
@Binding var search: String
|
||||||
|
@Binding var reload: Bool
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
TextField("Search", text: $search)
|
TextField("Search", text: $search)
|
||||||
.textFieldStyle(SimpleSearchStyle(trash: true, text: $search))
|
.textFieldStyle(SimpleSearchStyle(trash: true, reload: $reload, text: $search))
|
||||||
}
|
}
|
||||||
.frame(height: 30)
|
.frame(height: 30)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
//
|
|
||||||
// TrashView.swift
|
|
||||||
// Pearcleaner
|
|
||||||
//
|
|
||||||
// Created by Alin Lupascu on 11/11/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct TrashView: View {
|
|
||||||
@EnvironmentObject var appState: AppState
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
VStack() {
|
|
||||||
Text("Trash view my boi")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
//
|
||||||
|
// NewWin.swift
|
||||||
|
// Pearcleaner
|
||||||
|
//
|
||||||
|
// Created by Alin Lupascu on 11/16/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
class NewWin: NSWindowController {
|
||||||
|
|
||||||
|
static var shared = NewWin()
|
||||||
|
|
||||||
|
static func show(appState: AppState, width: CGFloat, height: CGFloat, newWin: NewWindow) {
|
||||||
|
shared = NewWin(appState: appState, width: width, height: height, newWin: newWin)
|
||||||
|
shared.window?.makeKeyAndOrderFront(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func close() {
|
||||||
|
shared.window?.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
convenience init(appState: AppState, width: CGFloat, height: CGFloat, newWin: NewWindow) {
|
||||||
|
|
||||||
|
let window = Self.makeWindow(width: width, height: height)
|
||||||
|
|
||||||
|
window.backgroundColor = NSColor.controlBackgroundColor
|
||||||
|
|
||||||
|
self.init(window: window)
|
||||||
|
|
||||||
|
// Using Visual Effect to make titlebar fully transparent
|
||||||
|
let visualEffect = NSVisualEffectView()
|
||||||
|
visualEffect.blendingMode = .behindWindow
|
||||||
|
visualEffect.state = .active
|
||||||
|
visualEffect.material = .underWindowBackground
|
||||||
|
|
||||||
|
let contentView = makeNewView(appState: appState, newWin: newWin)
|
||||||
|
|
||||||
|
let hostView = NSHostingView(rootView: contentView)
|
||||||
|
|
||||||
|
window.contentView = visualEffect
|
||||||
|
|
||||||
|
visualEffect.addSubview(hostView)
|
||||||
|
hostView.frame = visualEffect.frame
|
||||||
|
window.titleVisibility = .hidden
|
||||||
|
window.titlebarAppearsTransparent = true
|
||||||
|
window.center()
|
||||||
|
window.title = "Support Admin"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func makeWindow(width: CGFloat, height: CGFloat) -> NSWindow {
|
||||||
|
let contentRect = NSRect(x: 0, y: 0, width: width, height: height)
|
||||||
|
let styleMask: NSWindow.StyleMask = [
|
||||||
|
.titled,
|
||||||
|
.fullSizeContentView,
|
||||||
|
// .closable,
|
||||||
|
]
|
||||||
|
return NSWindow(contentRect: contentRect,
|
||||||
|
styleMask: styleMask,
|
||||||
|
backing: .buffered,
|
||||||
|
defer: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func makeNewView(appState: AppState, newWin: NewWindow) -> some View {
|
||||||
|
switch newWin {
|
||||||
|
case .update:
|
||||||
|
return AnyView(UpdateView()
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.environmentObject(appState))
|
||||||
|
case .no_update:
|
||||||
|
return AnyView(NoUpdateView()
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.environmentObject(appState))
|
||||||
|
case .perm:
|
||||||
|
return AnyView(PermView()
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.environmentObject(appState))
|
||||||
|
// case .restart:
|
||||||
|
// return AnyView(RestartView()
|
||||||
|
// .frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
// .environmentObject(appState))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
//
|
||||||
|
// PermView.swift
|
||||||
|
// Pearcleaner
|
||||||
|
//
|
||||||
|
// Created by Alin Lupascu on 11/16/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct PermView: View {
|
||||||
|
@EnvironmentObject var appState: AppState
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Text("Attention")
|
||||||
|
.font(.title)
|
||||||
|
.bold()
|
||||||
|
.padding(.vertical)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
}
|
||||||
|
.background(.red)
|
||||||
|
|
||||||
|
Text("Pearcleaner requires the following permissions:")
|
||||||
|
.font(.title3)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
// .padding(.horizontal, 20)
|
||||||
|
.padding(.vertical)
|
||||||
|
// .padding(.bottom)
|
||||||
|
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
Text(" - Full Disk Access permission to find and delete files in non-user locations.\n - Accessibility permission to delete files via Finder which allows for the Undo function.\n\nAdd Pearcleaner in both Privacy panes via the + or by dragging the app over the pane. If the app is already pre-populated in the list, just toggle On. Restart app when both permissions are granted.")
|
||||||
|
.font(.body)
|
||||||
|
.multilineTextAlignment(.leading)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
}
|
||||||
|
.frame(height: 130)
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") {
|
||||||
|
NSWorkspace.shared.open(url)
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Text("Full Disk Access")
|
||||||
|
}
|
||||||
|
.buttonStyle(WindowActionButton(action: .accept))
|
||||||
|
.padding()
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") {
|
||||||
|
NSWorkspace.shared.open(url)
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Text("Accessibility")
|
||||||
|
}
|
||||||
|
.buttonStyle(WindowActionButton(action: .accept))
|
||||||
|
.padding()
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
// NewWin.close
|
||||||
|
relaunchApp(afterDelay: 1)
|
||||||
|
}) {
|
||||||
|
Text("Restart")
|
||||||
|
}
|
||||||
|
.buttonStyle(WindowActionButton(action: .cancel))
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
.padding(EdgeInsets(top: -25, leading: 0, bottom: 25, trailing: 0))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
//
|
||||||
|
// UpdateView.swift
|
||||||
|
// Pearcleaner
|
||||||
|
//
|
||||||
|
// Created by Alin Lupascu on 11/16/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct UpdateView: View {
|
||||||
|
@EnvironmentObject var appState: AppState
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
.frame(width: appState.progressBar.1 != 1.0 ? 150 : 180)
|
||||||
|
|
||||||
|
Text("\(appState.progressBar.1 != 1.0 ? "Update Available 🥳" : "Completed 🚀")")
|
||||||
|
.font(.title)
|
||||||
|
.bold()
|
||||||
|
.padding(.vertical)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Text("v\(appState.releases.first?.tag_name ?? "")")
|
||||||
|
.font(.title3)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundStyle(Color.white.opacity(0.5))
|
||||||
|
.padding()
|
||||||
|
|
||||||
|
}
|
||||||
|
.background(.blue)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
Text("\(appState.releases.first?.body ?? "No release information")")
|
||||||
|
.font(.body)
|
||||||
|
.multilineTextAlignment(.leading)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
}
|
||||||
|
.frame(height: 160)
|
||||||
|
.padding()
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
|
||||||
|
VStack() {
|
||||||
|
ProgressView("\(appState.progressBar.0)", value: appState.progressBar.1, total: 1.0)
|
||||||
|
.progressViewStyle(LinearProgressViewStyle(tint: .blue))
|
||||||
|
.frame(height: 10)
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
|
||||||
|
|
||||||
|
HStack(alignment: .center, spacing: 20) {
|
||||||
|
Button(action: NewWin.close) {
|
||||||
|
Text("Cancel")
|
||||||
|
}
|
||||||
|
.buttonStyle(WindowActionButton(action: .cancel))
|
||||||
|
|
||||||
|
if appState.progressBar.1 != 1.0 {
|
||||||
|
Button(action: {
|
||||||
|
downloadUpdate(appState: appState)
|
||||||
|
}) {
|
||||||
|
Text("Update")
|
||||||
|
}
|
||||||
|
.buttonStyle(WindowActionButton(action: .accept))
|
||||||
|
} else {
|
||||||
|
Button(action: {
|
||||||
|
NewWin.close()
|
||||||
|
relaunchApp(afterDelay: 1)
|
||||||
|
}) {
|
||||||
|
Text("Restart")
|
||||||
|
}
|
||||||
|
.buttonStyle(WindowActionButton(action: .accept))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.padding(.bottom)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
.padding(EdgeInsets(top: -25, leading: 0, bottom: 25, trailing: 0))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct NoUpdateView: View {
|
||||||
|
@EnvironmentObject var appState: AppState
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
.frame(width: appState.progressBar.1 != 1.0 ? 140 : 180)
|
||||||
|
|
||||||
|
Text("\(appState.progressBar.1 != 1.0 ? "No Update Available 😌" : "Completed 🚀")")
|
||||||
|
.font(.title)
|
||||||
|
.bold()
|
||||||
|
.padding(.vertical)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Text("v\(appState.releases.first?.tag_name ?? "")")
|
||||||
|
.font(.title3)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundStyle(Color.white.opacity(0.5))
|
||||||
|
.padding()
|
||||||
|
|
||||||
|
}
|
||||||
|
.background(.blue)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Text("Pearcleaner is on the most current release available, but you may force a re-download of the same version below.")
|
||||||
|
.font(.body)
|
||||||
|
.lineLimit(3)
|
||||||
|
.multilineTextAlignment(.leading)
|
||||||
|
.padding(20)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
VStack() {
|
||||||
|
ProgressView("\(appState.progressBar.0)", value: appState.progressBar.1, total: 1.0)
|
||||||
|
.progressViewStyle(LinearProgressViewStyle(tint: .blue))
|
||||||
|
.frame(height: 10)
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
|
||||||
|
HStack(alignment: .center, spacing: 20) {
|
||||||
|
if appState.progressBar.1 < 0.1 {
|
||||||
|
Button(action: {
|
||||||
|
downloadUpdate(appState: appState)
|
||||||
|
}) {
|
||||||
|
Text("Force Update")
|
||||||
|
}
|
||||||
|
.buttonStyle(WindowActionButton(action: .cancel))
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
NewWin.close()
|
||||||
|
}) {
|
||||||
|
Text("Okay")
|
||||||
|
}
|
||||||
|
.buttonStyle(WindowActionButton(action: .accept))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if appState.progressBar.1 == 1.0 {
|
||||||
|
Button(action: {
|
||||||
|
NewWin.close()
|
||||||
|
relaunchApp()
|
||||||
|
}) {
|
||||||
|
Text("Restart")
|
||||||
|
}
|
||||||
|
.buttonStyle(WindowActionButton(action: .accept))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.bottom)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
.padding(EdgeInsets(top: -25, leading: 0, bottom: 25, trailing: 0))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user