Browse Source

Add Settings option for discord rich presence

pull/57/head
Ionite 1 year ago
parent
commit
a69ae1422c
No known key found for this signature in database
  1. 4
      StabilityMatrix.Avalonia/App.axaml.cs
  2. 172
      StabilityMatrix.Avalonia/Services/DiscordRichPresenceService.cs
  3. 2
      StabilityMatrix.Avalonia/Services/IDiscordRichPresenceService.cs
  4. 3
      StabilityMatrix.Avalonia/ViewModels/LaunchPageViewModel.cs
  5. 10
      StabilityMatrix.Avalonia/ViewModels/MainWindowViewModel.cs
  6. 11
      StabilityMatrix.Avalonia/ViewModels/SettingsViewModel.cs
  7. 26
      StabilityMatrix.Avalonia/Views/SettingsPage.axaml
  8. 8
      StabilityMatrix.Core/Helper/EventManager.cs

4
StabilityMatrix.Avalonia/App.axaml.cs

@ -149,9 +149,6 @@ public sealed class App : Application
mainWindow.ExtendClientAreaChromeHints = Program.Args.NoWindowChromeEffects ?
ExtendClientAreaChromeHints.NoChrome : ExtendClientAreaChromeHints.PreferSystemChrome;
// Initialize rich presence
Services.GetRequiredService<IDiscordRichPresenceService>().Initialize();
var settingsManager = Services.GetRequiredService<ISettingsManager>();
var windowSettings = settingsManager.Settings.WindowSettings;
@ -210,6 +207,7 @@ public sealed class App : Application
services.AddSingleton<MainWindowViewModel>(provider =>
new MainWindowViewModel(provider.GetRequiredService<ISettingsManager>(),
provider.GetRequiredService<IDiscordRichPresenceService>(),
provider.GetRequiredService<ServiceManager<ViewModelBase>>())
{
Pages =

172
StabilityMatrix.Avalonia/Services/DiscordRichPresenceService.cs

@ -1,7 +1,11 @@
using System;
using DiscordRPC;
using DiscordRPC.Logging;
using DiscordRPC.Message;
using Microsoft.Extensions.Logging;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Models.Packages;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.Services;
@ -10,51 +14,163 @@ public class DiscordRichPresenceService : IDiscordRichPresenceService
private const string ApplicationId = "1134669805237059615";
private readonly ILogger<DiscordRichPresenceService> logger;
private DiscordRpcClient? client;
public DiscordRichPresenceService(ILogger<DiscordRichPresenceService> logger)
private readonly ISettingsManager settingsManager;
private readonly DiscordRpcClient client;
private readonly string appDetails;
private bool isDisposed;
private RichPresence DefaultPresence => new()
{
this.logger = logger;
}
Details = appDetails,
Assets = new DiscordRPC.Assets
{
LargeImageKey = "stabilitymatrix-logo-1",
LargeImageText = $"Stability Matrix {appDetails}",
},
Buttons = new[]
{
new Button
{
Label = "GitHub",
Url = "https://github.com/LykosAI/StabilityMatrix",
}
}
};
public void Initialize()
public DiscordRichPresenceService(
ILogger<DiscordRichPresenceService> logger,
ISettingsManager settingsManager)
{
if (client != null) return;
this.logger = logger;
this.settingsManager = settingsManager;
appDetails = $"v{Compat.AppVersion.WithoutMetadata()}";
client = new DiscordRpcClient(ApplicationId);
client.Logger = new NullLogger();
client.OnReady += OnReady;
client.OnError += OnError;
client.OnClose += OnClose;
client.OnPresenceUpdate += OnPresenceUpdate;
client.OnReady += (sender, e) =>
{
logger.LogInformation("Received Ready from user {User}", e.User.Username);
};
client.OnPresenceUpdate += (sender, e) =>
settingsManager.SettingsPropertyChanged += (sender, args) =>
{
logger.LogInformation("Received Update: {Presence}", e.Presence.ToString());
if (args.PropertyName == nameof(settingsManager.Settings.IsDiscordRichPresenceEnabled))
{
UpdateState();
}
};
// Connect to the RPC
client.Initialize();
EventManager.Instance.RunningPackageStatusChanged += OnRunningPackageStatusChanged;
}
private void OnReady(object sender, ReadyMessage args)
{
logger.LogInformation("Received Ready from user {User}", args.User.Username);
}
private void OnError(object sender, ErrorMessage args)
{
logger.LogWarning("Received Error: {Message}", args.Message);
}
private void OnClose(object sender, CloseMessage args)
{
logger.LogInformation("Received Close: {Reason}", args.Reason);
}
private void OnPresenceUpdate(object sender, PresenceMessage args)
{
logger.LogDebug("Received Update: {Presence}", args.Presence.ToString());
}
private void OnRunningPackageStatusChanged(object? sender, RunningPackageStatusChangedEventArgs args)
{
if (!client.IsInitialized || !settingsManager.Settings.IsDiscordRichPresenceEnabled) return;
// Set rich presence
client.SetPresence(new RichPresence
if (args.CurrentPackagePair is null)
{
client.SetPresence(DefaultPresence);
}
else
{
var presence = DefaultPresence;
var packageTitle = args.CurrentPackagePair.BasePackage switch
{
A3WebUI => "Automatic1111 Web UI",
VladAutomatic => "SD.Next Web UI",
ComfyUI => "ComfyUI",
VoltaML => "VoltaML",
InvokeAI => "InvokeAI",
_ => "Stable Diffusion"
};
presence.State = $"Running {packageTitle}";
presence.Assets.SmallImageText = presence.State;
presence.Assets.SmallImageKey = args.CurrentPackagePair.BasePackage switch
{
ComfyUI => "fa_diagram_project",
VoltaML => "ic_package_voltaml",
InvokeAI => "ic_package_invokeai",
_ => "ic_fluent_box_512_filled"
};
presence.WithTimestamps(new Timestamps
{
StartUnixMilliseconds = (ulong?) DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
});
client.SetPresence(presence);
}
}
public void UpdateState()
{
// Set initial rich presence
if (settingsManager.Settings.IsDiscordRichPresenceEnabled)
{
Details = "Stability Matrix",
State = "In the main menu",
Assets = new DiscordRPC.Assets
lock (client)
{
LargeImageKey = "stabilitymatrix-logo-1",
LargeImageText = "Stability Matrix",
SmallImageKey = "stabilitymatrix-logo-1",
SmallImageText = "Stability Matrix"
if (!client.IsInitialized)
{
client.Initialize();
client.SetPresence(DefaultPresence);
}
}
});
}
else
{
lock (client)
{
if (client.IsInitialized)
{
client.ClearPresence();
client.Deinitialize();
}
}
}
}
public void Dispose()
{
client?.Dispose();
if (!isDisposed)
{
if (client.IsInitialized)
{
client.ClearPresence();
}
client.Dispose();
EventManager.Instance.RunningPackageStatusChanged -= OnRunningPackageStatusChanged;
}
isDisposed = true;
GC.SuppressFinalize(this);
}
~DiscordRichPresenceService()
{
Dispose();
}
}

2
StabilityMatrix.Avalonia/Services/IDiscordRichPresenceService.cs

@ -4,5 +4,5 @@ namespace StabilityMatrix.Avalonia.Services;
public interface IDiscordRichPresenceService : IDisposable
{
public void Initialize();
public void UpdateState();
}

3
StabilityMatrix.Avalonia/ViewModels/LaunchPageViewModel.cs

@ -249,6 +249,8 @@ public partial class LaunchPageViewModel : PageViewModelBase, IDisposable, IAsyn
await basePackage.RunPackage(packagePath, command, userArgsString);
RunningPackage = basePackage;
EventManager.Instance.OnRunningPackageStatusChanged(new PackagePair(activeInstall, basePackage));
}
// Unpacks sitecustomize.py to the target venv
@ -388,6 +390,7 @@ public partial class LaunchPageViewModel : PageViewModelBase, IDisposable, IAsyn
private void OnProcessExited(object? sender, int exitCode)
{
EventManager.Instance.OnRunningPackageStatusChanged(null);
Dispatcher.UIThread.InvokeAsync(async () =>
{
logger.LogTrace("Process exited ({Code}) at {Time:g}",

10
StabilityMatrix.Avalonia/ViewModels/MainWindowViewModel.cs

@ -23,6 +23,7 @@ public partial class MainWindowViewModel : ViewModelBase
{
private readonly ISettingsManager settingsManager;
private readonly ServiceManager<ViewModelBase> dialogFactory;
private readonly IDiscordRichPresenceService discordRichPresenceService;
public string Greeting => "Welcome to Avalonia!";
[ObservableProperty]
@ -40,10 +41,14 @@ public partial class MainWindowViewModel : ViewModelBase
public ProgressManagerViewModel ProgressManagerViewModel { get; init; }
public UpdateViewModel UpdateViewModel { get; init; }
public MainWindowViewModel(ISettingsManager settingsManager, ServiceManager<ViewModelBase> dialogFactory)
public MainWindowViewModel(
ISettingsManager settingsManager,
IDiscordRichPresenceService discordRichPresenceService,
ServiceManager<ViewModelBase> dialogFactory)
{
this.settingsManager = settingsManager;
this.dialogFactory = dialogFactory;
this.discordRichPresenceService = discordRichPresenceService;
ProgressManagerViewModel = dialogFactory.Get<ProgressManagerViewModel>();
UpdateViewModel = dialogFactory.Get<UpdateViewModel>();
@ -74,6 +79,9 @@ public partial class MainWindowViewModel : ViewModelBase
return;
}
// Initialize Discord Rich Presence (this needs LibraryDir so is set here)
discordRichPresenceService.UpdateState();
// Index checkpoints if we dont have
Task.Run(() => settingsManager.IndexCheckpoints()).SafeFireAndForget();

11
StabilityMatrix.Avalonia/ViewModels/SettingsViewModel.cs

@ -44,6 +44,7 @@ public partial class SettingsViewModel : PageViewModelBase
private readonly IPrerequisiteHelper prerequisiteHelper;
private readonly IPyRunner pyRunner;
private readonly ServiceManager<ViewModelBase> dialogFactory;
public SharedState SharedState { get; }
public override string Title => "Settings";
@ -66,6 +67,9 @@ public partial class SettingsViewModel : PageViewModelBase
// Shared folder options
[ObservableProperty] private bool removeSymlinksOnShutdown;
// Integrations section
[ObservableProperty] private bool isDiscordRichPresenceEnabled;
// Debug section
[ObservableProperty] private string? debugPaths;
[ObservableProperty] private string? debugCompatInfo;
@ -90,14 +94,19 @@ public partial class SettingsViewModel : PageViewModelBase
this.prerequisiteHelper = prerequisiteHelper;
this.pyRunner = pyRunner;
this.dialogFactory = dialogFactory;
SharedState = sharedState;
SelectedTheme = settingsManager.Settings.Theme ?? AvailableThemes[1];
RemoveSymlinksOnShutdown = settingsManager.Settings.RemoveFolderLinksOnShutdown;
settingsManager.RelayPropertyFor(this,
vm => vm.SelectedTheme,
settings => settings.Theme);
settingsManager.RelayPropertyFor(this,
vm => vm.IsDiscordRichPresenceEnabled,
settings => settings.IsDiscordRichPresenceEnabled);
}
partial void OnSelectedThemeChanged(string? value)

26
StabilityMatrix.Avalonia/Views/SettingsPage.axaml

@ -6,7 +6,7 @@
xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData"
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="700"
x:DataType="vm:SettingsViewModel"
x:CompileBindings="True"
d:DataContext="{x:Static mocks:DesignData.SettingsViewModel}"
@ -53,7 +53,7 @@
Description="Select this option if you're having problems moving Stability Matrix to another drive"
Margin="8,8">
<ui:SettingsExpander.Footer>
<CheckBox Margin="8"
<CheckBox Margin="4"
IsChecked="{Binding RemoveSymlinksOnShutdown}"/>
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
@ -108,6 +108,28 @@
</StackPanel>
</Grid>
<!-- Integrations -->
<Grid RowDefinitions="auto,*">
<TextBlock
FontWeight="Medium"
Text="Integrations"
Margin="0,16,0,8" />
<StackPanel Grid.Row="1">
<ui:SettingsExpander
Header="Discord Rich Presence"
Margin="8,0">
<ui:SettingsExpander.IconSource>
<controls:FASymbolIconSource Symbol="fa-brands fa-discord"/>
</ui:SettingsExpander.IconSource>
<ui:SettingsExpander.Footer>
<ToggleSwitch
IsChecked="{Binding IsDiscordRichPresenceEnabled}"
Margin="4" />
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
</StackPanel>
</Grid>
<!-- System Options -->
<Grid RowDefinitions="auto,*">
<TextBlock

8
StabilityMatrix.Core/Helper/EventManager.cs

@ -1,8 +1,11 @@
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Models.Update;
namespace StabilityMatrix.Core.Helper;
public record struct RunningPackageStatusChangedEventArgs(PackagePair? CurrentPackagePair);
public class EventManager
{
public static EventManager Instance { get; } = new();
@ -22,6 +25,7 @@ public class EventManager
public event EventHandler<Guid> PackageLaunchRequested;
public event EventHandler? ScrollToBottomRequested;
public event EventHandler<ProgressItem>? ProgressChanged;
public event EventHandler<RunningPackageStatusChangedEventArgs>? RunningPackageStatusChanged;
public void OnGlobalProgressChanged(int progress) => GlobalProgressChanged?.Invoke(this, progress);
public void RequestPageChange(Type pageType) => PageChangeRequested?.Invoke(this, pageType);
@ -36,4 +40,6 @@ public class EventManager
ScrollToBottomRequested?.Invoke(this, EventArgs.Empty);
public void OnProgressChanged(ProgressItem progress) =>
ProgressChanged?.Invoke(this, progress);
public void OnRunningPackageStatusChanged(PackagePair? currentPackagePair) =>
RunningPackageStatusChanged?.Invoke(this, new RunningPackageStatusChangedEventArgs(currentPackagePair));
}

Loading…
Cancel
Save