Updated EllieHub to 1.0.3.0

This commit is contained in:
Toastie (DCS Team) 2024-07-02 12:45:09 +12:00
parent 7429e0298a
commit 2c37112b4a
Signed by: toastie_t0ast
GPG key ID: 27F3B6855AFD40A4
8 changed files with 76 additions and 47 deletions

View file

@ -8,7 +8,6 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<WarningsAsErrors>Nullable</WarningsAsErrors>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<BuiltInComInteropSupport>True</BuiltInComInteropSupport>
@ -16,11 +15,12 @@
<!--Publishing-->
<PublishSingleFile>true</PublishSingleFile>
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
<SelfContained>true</SelfContained>
<DebugType>embedded</DebugType>
<!--Version-->
<VersionPrefix>1.0.2.0</VersionPrefix>
<VersionPrefix>1.0.3.0</VersionPrefix>
<!--Avalonia Settings-->
<ApplicationManifest>app.manifest</ApplicationManifest>
@ -30,6 +30,13 @@
<UseAppHost>true</UseAppHost>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />

View file

@ -83,8 +83,8 @@ public static class WindowExt
{
return (!activeView.TryFindResource(resourceName, theme, out var resource))
? throw new InvalidOperationException($"Resource '{resourceName}' was not found.")
: (!Utilities.TryCastTo<T>(resource, out var result))
? throw new InvalidCastException($"Could not convert resource of type '{resource?.GetType()?.FullName}' to '{nameof(T)}'.")
: result;
: (resource is T castResource)
? castResource
: throw new InvalidCastException($"Could not convert resource of type '{resource?.GetType()?.FullName}' to '{nameof(T)}'.");
}
}

View file

@ -20,7 +20,7 @@ namespace EllieHub.Features.AppConfig.ViewModels;
/// </summary>
public class ConfigViewModel : ViewModelBase<ConfigView>
{
private static readonly string _unixNotice = Environment.OSVersion.Platform is not PlatformID.Unix
private static readonly string _unixNotice = (Environment.OSVersion.Platform is not PlatformID.Unix)
? string.Empty
: Environment.NewLine + "To make the dependencies accessible to your bot instances without this updater, consider installing " +
$"them through your package manager or adding the directory \"{AppStatics.AppDepsUri}\" to your PATH environment variable.";

View file

@ -69,10 +69,10 @@ public partial class LateralBarView : ReactiveUserControl<LateralBarViewModel>
/// <exception cref="InvalidOperationException">Occurs when the visual tree has an unexpected structure.</exception>
public void ApplyBotButtonBorder(Button button)
{
if (!Utilities.TryCastTo<Border>(button.Parent?.Parent, out var border))
if (button.Parent?.Parent is not Border border)
throw new InvalidOperationException("Visual tree has an unexpected structure.");
if (!Utilities.TryCastTo<ImmutableSolidColorBrush>(this.FindResource(base.ActualThemeVariant, "BotSelectionColor"), out var resourceColor))
if (this.FindResource(base.ActualThemeVariant, "BotSelectionColor") is not ImmutableSolidColorBrush resourceColor)
return;
border.BorderBrush = resourceColor;
@ -95,7 +95,7 @@ public partial class LateralBarView : ReactiveUserControl<LateralBarViewModel>
private void LoadBotViewModel(object sender, RoutedEventArgs eventArgs)
{
// "sender", for some reason, is not one of the buttons stored in the lateral bar's view-model.
if (Utilities.TryCastTo<Button>(sender, out var button) && this.ViewModel!.BotButtonList.First(x => x.Content == button.Content).IsEnabled)
if (sender is Button button && this.ViewModel!.BotButtonList.First(x => x.Content == button.Content).IsEnabled)
BotButtonClick?.Invoke(button, eventArgs);
}
@ -108,10 +108,11 @@ public partial class LateralBarView : ReactiveUserControl<LateralBarViewModel>
/// <exception cref="InvalidOperationException">Occurs when the visual tree has an unexpected structure.</exception>
private void OnBotButtonLoad(object? sender, VisualTreeAttachmentEventArgs eventArgs)
{
if (!Utilities.TryCastTo<Panel>(sender, out var panel)
|| !Utilities.TryCastTo<SKImageView>(((Border)panel.Children[0]).Child, out var botAvatar)
|| !Utilities.TryCastTo<Button>(panel.Children[1], out var button)
|| !Utilities.TryCastTo<Guid>(button.Content, out var botId))
if (sender is not Panel panel
|| panel.Children[0] is not Border border
|| border.Child is not SKImageView botAvatar
|| panel.Children[1] is not Button button
|| button.Content is not Guid botId)
throw new InvalidOperationException("Visual tree has an unexpected structure.");
// Set the avatar
@ -132,7 +133,7 @@ public partial class LateralBarView : ReactiveUserControl<LateralBarViewModel>
/// <exception cref="InvalidOperationException">Occurs when <paramref name="sender"/> is not a <see cref="Button"/>.</exception>
private void DownsizeBotAvatar(object? sender, PointerPressedEventArgs eventArgs)
{
if (!Utilities.TryCastTo<Button>(sender, out var button))
if (sender is not Button button)
throw new InvalidOperationException($"Sender is not a {nameof(Button)}.");
var botAvatar = FindAvatarComponent(button);
@ -149,7 +150,7 @@ public partial class LateralBarView : ReactiveUserControl<LateralBarViewModel>
/// <exception cref="InvalidOperationException">Occurs when <paramref name="sender"/> is not a <see cref="Button"/>.</exception>
private void UpsizeBotAvatar(object? sender, PointerReleasedEventArgs eventArgs)
{
if (!Utilities.TryCastTo<Button>(sender, out var button))
if (sender is not Button button)
throw new InvalidOperationException($"Sender is not a {nameof(Button)}.");
var botAvatar = FindAvatarComponent(button);
@ -167,7 +168,7 @@ public partial class LateralBarView : ReactiveUserControl<LateralBarViewModel>
/// <exception cref="InvalidOperationException">Occurs when the component's content is not a Guid.</exception>
private SKImageView FindAvatarComponent<T>(T component) where T : ContentControl
{
return (!Utilities.TryCastTo<Guid>(component.Content, out var botId))
return (component.Content is not Guid botId)
? throw new InvalidOperationException($"{nameof(T)} does not contain a bot Id.")
: FindAvatarComponent(botId);
}

View file

@ -293,7 +293,7 @@ public partial class AppView : ReactiveWindow<AppViewModel>
private static BotConfigViewModel GetBotConfigViewModel(Button button, IServiceScopeFactory scopeFactory)
{
using var scope = scopeFactory.CreateScope();
var botId = (Guid)(button.Content ?? throw new InvalidOperationException("Bot button has no valid Id."));
var botId = (button.Content ?? throw new InvalidOperationException("Bot button has no valid Id."));
var botResolver = scope.ServiceProvider.GetParameterizedService<EllieResolver>(botId);
return scope.ServiceProvider.GetParameterizedService<BotConfigViewModel>(botResolver);

View file

@ -115,33 +115,9 @@ public sealed partial class EllieResolver : IBotResolver
return null;
}
if (!string.IsNullOrWhiteSpace(botEntry.Version))
return botEntry.Version;
// Ellie is published as a single-file binary, so we have to extract
// its contents first in order to read the assembly for its version.
using var executableReader = new ExecutableReader(executableUri);
var extractDirectoryUri = Path.Combine(_tempDirectory, "EllieBotExtract");
var extractAssemblyUri = Path.Combine(extractDirectoryUri, "EllieBot.dll");
try
{
await executableReader.ExtractToDirectoryAsync(extractDirectoryUri, cToken);
var nadekoAssembly = Assembly.LoadFile(extractAssemblyUri);
var version = nadekoAssembly.GetName().Version
?? throw new InvalidOperationException($"Could not find version of the assembly at {extractAssemblyUri}.");
var currentVersion = $"{version.Major}.{version.Minor}.{version.Build}";
await _appConfigManager.UpdateBotEntryAsync(Id, x => x with { Version = currentVersion }, cToken);
return currentVersion;
}
finally
{
Utilities.TryDeleteDirectory(extractDirectoryUri);
}
return (string.IsNullOrWhiteSpace(botEntry.Version))
? await GetBotVersionFromAssemblyAsync(executableUri, cToken)
: botEntry.Version;
}
/// <inheritdoc/>
@ -397,6 +373,51 @@ public sealed partial class EllieResolver : IBotResolver
return response;
}
/// <summary>
/// Gets the bot version from the bot's assembly.
/// </summary>
/// <param name="executableUri">The path to the bot's executable file.</param>
/// <param name="cToken">The cancellation token.</param>
/// <returns>The version of the bot or <see langword="null"/> if the executable file is not found.</returns>
/// <exception cref="InvalidOperationException">Occurs when the assembly file is not found.</exception>
private async ValueTask<string?> GetBotVersionFromAssemblyAsync(string executableUri, CancellationToken cToken)
{
if (!File.Exists(executableUri))
return null;
var directoryUri = Directory.GetParent(executableUri)?.FullName ?? Path.GetPathRoot(executableUri)!;
var assemblyUri = Path.Join(directoryUri, "EllieBot.dll");
var isSingleFile = !File.Exists(assemblyUri);
// If Ellie is published as a single-file binary, we have to extract
// its contents first in order to read the assembly for its version.
if (isSingleFile)
{
directoryUri = Path.Join(_tempDirectory, "EllieBotExtract_" + DateTimeOffset.Now.Ticks);
assemblyUri = Path.Join(directoryUri, "EllieBot.dll");
using var executableReader = new ExecutableReader(executableUri);
await executableReader.ExtractToDirectoryAsync(directoryUri, cToken);
}
try
{
var ellieAssembly = Assembly.LoadFile(assemblyUri);
var version = ellieAssembly.GetName().Version
?? throw new InvalidOperationException($"Could not find version for the assembly at {assemblyUri}.");
var currentVersion = $"{version.Major}.{version.Minor}.{version.Build}";
await _appConfigManager.UpdateBotEntryAsync(Id, x => x with { Version = currentVersion }, cToken);
return currentVersion;
}
finally
{
if (isSingleFile)
Utilities.TryDeleteDirectory(directoryUri);
}
}
[GeneratedRegex(@"^(?:\S+\-)(\S+\-\S+)\-", RegexOptions.Compiled)]
private static partial Regex GenerateUnzipedDirRegex();
}

View file

@ -26,7 +26,7 @@ public partial class BotConfigView : ReactiveUserControl<BotConfigViewModel>
/// <param name="eventArgs">The event arguments.</param>
private void AvatarButtonHover(object? sender, PointerEventArgs eventArgs)
{
if (!Utilities.TryCastTo<Button>(sender, out var button))
if (sender is not Button button)
return;
button.Opacity = 3.0;
@ -40,7 +40,7 @@ public partial class BotConfigView : ReactiveUserControl<BotConfigViewModel>
/// <param name="eventArgs">The event arguments.</param>
private void AvatarButtonUnhover(object? sender, PointerEventArgs eventArgs)
{
if (!Utilities.TryCastTo<Button>(sender, out var button))
if (sender is not Button button)
return;
button.Opacity = 0.0;

View file

@ -142,7 +142,7 @@ public sealed class AppResolver : IAppResolver
// Rename the original file from "file" to "file_old".
if (File.Exists(destinationUri))
File.Move(destinationUri, destinationUri + OldFileSuffix);
File.Move(destinationUri, destinationUri + OldFileSuffix, true);
// Move the new file to the application's directory.
if (Environment.OSVersion.Platform is not PlatformID.Unix)