Skip to content

Commit

Permalink
Merge pull request #52 from macadmins/dev
Browse files Browse the repository at this point in the history
v1.4.0
  • Loading branch information
almenscorner authored Nov 6, 2024
2 parents 361f65d + 40e11d9 commit d84a35f
Show file tree
Hide file tree
Showing 26 changed files with 370 additions and 202 deletions.
4 changes: 2 additions & 2 deletions App.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
x:Class="SupportCompanion.App"
x:DataType="vm:MainWindowViewModel"
Name="Support Companion"
xmlns:local="using:SupportCompanion"
xmlns:common="using:SupportCompanion.Common"
xmlns:sukiUi="clr-namespace:SukiUI;assembly=SukiUI"
xmlns:vm="clr-namespace:SupportCompanion.ViewModels"
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
RequestedThemeVariant="Default">

<Application.DataTemplates>
<local:ViewLocator />
<common:ViewLocator />
</Application.DataTemplates>
<TrayIcon.Icons>
<TrayIcons>
Expand Down
7 changes: 6 additions & 1 deletion App.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.DependencyInjection;
using ObjCRuntime;
using SukiUI.Dialogs;
using SukiUI.Toasts;
using SupportCompanion.Helpers;
using SupportCompanion.Models;
using SupportCompanion.Services;
Expand All @@ -27,6 +29,7 @@ public App()
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
RegisterAppServices();
var prefs = new AppConfigHelper();
try
{
Expand Down Expand Up @@ -76,7 +79,7 @@ private async Task InitializeCultureAsync()

public override async void OnFrameworkInitializationCompleted()
{
RegisterAppServices();
//RegisterAppServices();
await InitializeCultureAsync();

if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
Expand Down Expand Up @@ -161,6 +164,8 @@ private void RegisterAppServices()
serviceCollection.AddSingleton<LoggerService>();
serviceCollection.AddSingleton<NotificationService>();
serviceCollection.AddSingleton<ProfilerApplications>();
serviceCollection.AddSingleton<ISukiToastManager, SukiToastManager>();
serviceCollection.AddSingleton<ISukiDialogManager, SukiDialogManager>();

serviceCollection.AddSingleton<DeviceWidgetViewModel>();
serviceCollection.AddSingleton<MunkiPendingAppsViewModel>();
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.4.0] - 2024-11-06
### Changed
- Avalonia and SukiUI has been updated.
- As part of the SukiUI update, the SukiHost has been updated to use the new scalable style of hosts.
- Mainwindow height has been slightly increased.
- Uninstall script updated with a check for root and use of Apple best practices. #50 thanks @pboushy

### Fixed
- When a app or system update notification was clicked, the command was not run resulting in nothing happening.
- ToolTips were not being shown.

## [1.3.0] - 2024-09-19
### Added
- A new mode for the app called `SystemProfilerApps` which allows for the app to display applications installed under `/Applications` and their version numbers as well as Architecture. This mode is useful for admins who want to see what applications are installed on the device and their version numbers. To enable this mode, set `SystemProfilerApps` to `true` in the configuration. Example configuration:
Expand Down
40 changes: 40 additions & 0 deletions Common/ViewLocator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.ComponentModel;
using Avalonia.Controls;
using Avalonia.Controls.Templates;

namespace SupportCompanion.Common;

public class ViewLocator : IDataTemplate
{
private readonly Dictionary<object, Control> _controlCache = new();

public Control Build(object? data)
{
if (data is null)
return new TextBlock { Text = "Data is null." };

var fullName = data.GetType().FullName;

if (string.IsNullOrWhiteSpace(fullName))
return new TextBlock { Text = "Type has no name, or name is empty." };

var name = fullName.Replace("ViewModel", "View");
var type = Type.GetType(name);
if (type is null)
return new TextBlock { Text = $"No View For {name}." };

if (!_controlCache.TryGetValue(data, out var res))
{
res ??= (Control)Activator.CreateInstance(type)!;
_controlCache[data] = res;
}

res.DataContext = data;
return res;
}

public bool Match(object? data)
{
return data is INotifyPropertyChanged;
}
}
4 changes: 2 additions & 2 deletions Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.3.0</string>
<string>1.4.0</string>
<key>LSMinimumSystemVersion</key>
<string>10.15</string>
<key>CFBundleExecutable</key>
Expand All @@ -32,7 +32,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.3.0</string>
<string>1.4.0</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>LSUIElement</key>
Expand Down
9 changes: 7 additions & 2 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ internal sealed class Program
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
//[STAThread]
public static void Main(string[] args)
{
BuildAvaloniaApp()
Expand All @@ -25,7 +25,12 @@ public static AppBuilder BuildAvaloniaApp()
.UseReactiveUI()
.With(new MacOSPlatformOptions
{
ShowInDock = false, DisableDefaultApplicationMenuItems = true
ShowInDock = false,
DisableDefaultApplicationMenuItems = true
})
.With(new AvaloniaNativePlatformOptions
{
RenderingMode = new[] { AvaloniaNativeRenderingMode.OpenGl, AvaloniaNativeRenderingMode.Metal }
});
}
}
30 changes: 24 additions & 6 deletions Services/ActionsService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using SukiUI.Controls;
using Avalonia.Controls.Notifications;
using SukiUI.Toasts;
using SupportCompanion.Helpers;
using SupportCompanion.Interfaces;

Expand All @@ -12,11 +13,14 @@ public class ActionsService : IActions
private const string OpenMmcUpdates = "open munki://updates.html";
private readonly LoggerService _logger;

public ActionsService(LoggerService loggerService)
public ActionsService(LoggerService loggerService, ISukiToastManager toastManager)
{
_logger = loggerService;
ToastManager = toastManager;
}

public ISukiToastManager ToastManager { get; }

public async Task KillAgent()
{
var startInfo = new ProcessStartInfo
Expand All @@ -37,10 +41,20 @@ public async Task KillAgent()
if (process.ExitCode != 0)
{
_logger.Log("ActionsService:KillAgent", $"Failed to kill agent: {error}", 2);
await SukiHost.ShowToast("Kill Agent", "Failed to kill agent");
ToastManager.CreateSimpleInfoToast()
.WithTitle("Kill Agent")
.OfType(NotificationType.Error)
.WithContent("Failed to kill agent")
.Queue();
}
else
{
ToastManager.CreateSimpleInfoToast()
.WithTitle("Kill Agent")
.OfType(NotificationType.Success)
.WithContent("Agent successfully killed")
.Queue();
}

await SukiHost.ShowToast("Kill Agent", "Agent successfully killed");
}

public async Task Reboot()
Expand All @@ -63,7 +77,11 @@ public async Task Reboot()
if (process.ExitCode != 0)
{
_logger.Log("ActionsService:Reboot", $"Reboot failed: {error}", 2);
await SukiHost.ShowToast("Reboot", "Reboot failed");
ToastManager.CreateSimpleInfoToast()
.WithTitle("Reboot")
.OfType(NotificationType.Error)
.WithContent("Reboot failed")
.Queue();
}
}

Expand Down
3 changes: 1 addition & 2 deletions Services/NotificationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ public class NotificationService : INotification
{
private readonly LoggerService _logger;

public NotificationService(LoggerService loggerService)
public NotificationService(LoggerService loggerService) : this()
{
_logger = loggerService;
}

public NotificationService()
{
NSUserNotificationCenter.DefaultUserNotificationCenter.DidActivateNotification += (sender, args) =>
Expand Down
18 changes: 9 additions & 9 deletions SupportCompanion.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,19 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Avalonia" Version="11.1.3" />
<PackageReference Include="Avalonia.Desktop" Version="11.1.3" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.1.3" />
<PackageReference Include="Avalonia" Version="11.2.0" />
<PackageReference Include="Avalonia.Desktop" Version="11.2.0" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.0" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.3" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.1.3" />
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.2.0" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.2.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2"/>
<PackageReference Include="Material.Icons.Avalonia" Version="2.1.9"/>
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.5"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0"/>
<PackageReference Include="Material.Icons.Avalonia" Version="2.1.10" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0-rc.1.24451.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0-rc.1.24431.7" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
<PackageReference Include="PropertyList" Version="1.1.0"/>
<PackageReference Include="SukiUI" Version="6.0.0-beta4"/>
<PackageReference Include="SukiUI" Version="6.0.0-rc" />
<PackageReference Include="Dotnet.Bundle" Version="0.9.13"/>
</ItemGroup>

Expand Down
68 changes: 51 additions & 17 deletions ViewModels/ActionsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
using System.Text.Json;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Notifications;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using SukiUI.Controls;
using SukiUI.Dialogs;
using SukiUI.Toasts;
using SupportCompanion.Interfaces;
using SupportCompanion.Services;

Expand All @@ -24,10 +26,13 @@ public partial class ActionsViewModel : ObservableObject, IWindowStateAware
[ObservableProperty] private bool _hasUpdates;
[ObservableProperty] private string _updateCount = "0";

public ActionsViewModel(ActionsService actionsService, LoggerService loggerService)
public ActionsViewModel(ActionsService actionsService, LoggerService loggerService,
ISukiDialogManager dialogManager, ISukiToastManager toastManager)
{
_actionsService = actionsService;
_logger = loggerService;
ToastManager = toastManager;
DialogManager = dialogManager;
HideSupportButton = !App.Config.HiddenActions.Contains("Support");
HideMmcButton = !App.Config.HiddenActions.Contains("ManagedSoftwareCenter") && App.Config.MunkiMode;
HideChangePasswordButton = !App.Config.HiddenActions.Contains("ChangePassword");
Expand All @@ -38,6 +43,9 @@ public ActionsViewModel(ActionsService actionsService, LoggerService loggerServi
if (!App.Config.HiddenWidgets.Contains("Actions")) Dispatcher.UIThread.Post(InitializeAsync);
}

public ISukiToastManager ToastManager { get; }
public ISukiDialogManager DialogManager { get; }

public bool HideSupportButton { get; private set; }
public bool HideChangePasswordButton { get; private set; }
public bool HideMmcButton { get; private set; }
Expand Down Expand Up @@ -134,8 +142,11 @@ public async Task OpenChangePasswordPage()
// Do we have a network connection?
if (!await CheckForInternetConnection())
{
await SukiHost.ShowToast("Change Password",
"No network connection");
ToastManager.CreateSimpleInfoToast()
.WithTitle("Change Password")
.OfType(NotificationType.Warning)
.WithContent("No network connection")
.Queue();
return;
}

Expand All @@ -158,19 +169,30 @@ await SukiHost.ShowToast("Change Password",
// open the SSO extension with the realm name
await _actionsService.RunCommandWithoutOutput($"/usr/bin/app-sso -c {realmName}");
else
await SukiHost.ShowToast("Change Password",
$"Cannot reach {realmName}, make sure to connect to VPN or corporate network.");
ToastManager.CreateSimpleInfoToast()
.WithTitle("Change Password")
.OfType(NotificationType.Warning)
.WithContent($"Cannot reach {realmName}, make sure to connect to VPN or corporate network.")
.Queue();
}
catch (Exception)
{
await SukiHost.ShowToast("Change Password",
"Change request failed for unknown reason");
//await SukiHost.ShowToast("Change Password",
// "Change request failed for unknown reason");
ToastManager.CreateSimpleInfoToast()
.WithTitle("Change Password")
.OfType(NotificationType.Error)
.WithContent("Change request failed for unknown reason")
.Queue();
}
}
else
{
await SukiHost.ShowToast("Change Password",
"Change password mode not configured");
ToastManager.CreateSimpleInfoToast()
.WithTitle("Change Password")
.OfType(NotificationType.Warning)
.WithContent("Change password mode not configured")
.Queue();
}
}
}
Expand All @@ -187,8 +209,11 @@ public async Task GatherLogs()
// Check if the zip command was successful
if (!File.Exists(archivePath))
{
await SukiHost.ShowToast("Gather Logs",
"Failed to gather logs");
ToastManager.CreateSimpleInfoToast()
.WithTitle("Gather Logs")
.OfType(NotificationType.Error)
.WithContent("Failed to gather logs")
.Queue();
return;
}

Expand Down Expand Up @@ -216,20 +241,29 @@ await SukiHost.ShowToast("Gather Logs",

File.Delete(archivePath); // Delete the source file after successful copy

await SukiHost.ShowToast("Gather Logs",
"Logs saved successfully");
ToastManager.CreateSimpleInfoToast()
.WithTitle("Gather Logs")
.OfType(NotificationType.Success)
.WithContent("Logs saved successfully")
.Queue();
}
else
{
await SukiHost.ShowToast("Gather Logs",
"Logs not saved");
ToastManager.CreateSimpleInfoToast()
.WithTitle("Gather Logs")
.OfType(NotificationType.Warning)
.WithContent("Logs not saved")
.Queue();
}
}
}

public void ShowSupportInfoDialog()
{
SukiHost.ShowDialog(new SupportDialogViewModel(), allowBackgroundClose: true);
DialogManager.CreateDialog()
.WithViewModel(dialog => new SupportDialogViewModel())
.Dismiss().ByClickingBackground()
.TryShow();
}

private void CleanUp()
Expand Down
Loading

0 comments on commit d84a35f

Please sign in to comment.