Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions WheelWizard.Test/Features/AutoUpdating/AutoUpdaterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,33 @@ public async Task ReleaseLookupFailure_IsPresentedWithoutStartingAnUpdate()
Assert.Empty(fixture.Platform.ReceivedCalls());
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task PostponedUpdate_CanBeReopenedWithoutAnotherLookup(bool automatic)
{
var fixture = new Fixture();
fixture.Platform.SupportsAutomaticUpdate.Returns(automatic);
fixture.Releases([Release("v2.0.0")]);
var notifications = 0;
fixture.Service.UpdateAvailable += (_, _) => notifications++;

Assert.False(fixture.Service.IsUpdateAvailable);
await fixture.Service.ShowAvailableUpdateAsync();
Assert.Empty(fixture.Presentation.ReceivedCalls());

await fixture.Service.CheckForUpdatesAsync();
Assert.True(fixture.Service.IsUpdateAvailable);
Assert.Equal(1, notifications);
await fixture.Service.ShowAvailableUpdateAsync();

await fixture.GitHub.Received(1).GetReleasesAsync();
if (automatic)
await fixture.Presentation.Received(2).ConfirmUpdateAsync("2.0.0", "1.0.0");
else
await fixture.Presentation.Received(2).ShowManualUpdateAsync("2.0.0", "1.0.0");
}

private static GithubRelease Release(string version, bool prerelease = false, bool compatible = true) =>
new()
{
Expand Down
19 changes: 14 additions & 5 deletions WheelWizard.Test/Features/WiiManagement/RegionLicenseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,20 @@ private static (GameLicenseSingletonService, MockFileSystem, ISettingsManager) C
var settings = Substitute.For<ISettingsManager>();
var miis = Substitute.For<IMiiDbService>();
miis.GetByAvatarId(Arg.Any<uint>()).Returns(Fail("Missing Mii"));
var service = new GameLicenseSingletonService(miis, fs,
Substitute.For<IWhWzDataSingletonService>(), Substitute.For<IRrRatingReader>(), settings,
Substitute.For<ISaveRegionService>(), Substitute.For<IDolphinPaths>(), paths,
Substitute.For<IRoomPresence>(), Substitute.For<IPollingScheduler>(), System.TimeProvider.System,
NullLogger<GameLicenseSingletonService>.Instance);
var service = new GameLicenseSingletonService(
miis,
fs,
Substitute.For<IWhWzDataSingletonService>(),
Substitute.For<IRrRatingReader>(),
settings,
Substitute.For<ISaveRegionService>(),
Substitute.For<IDolphinPaths>(),
paths,
Substitute.For<IRoomPresence>(),
Substitute.For<IPollingScheduler>(),
System.TimeProvider.System,
NullLogger<GameLicenseSingletonService>.Instance
);
return (service, fs, settings);
}
}
53 changes: 39 additions & 14 deletions WheelWizard/Features/AutoUpdating/AutoUpdaterSingletonService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ namespace WheelWizard.AutoUpdating;

public interface IAutoUpdaterSingletonService
{
public Task CheckForUpdatesAsync();
bool IsUpdateAvailable { get; }
event EventHandler? UpdateAvailable;
Task CheckForUpdatesAsync();
Task ShowAvailableUpdateAsync();
}

public class AutoUpdaterSingletonService(
Expand All @@ -19,6 +22,10 @@ IUpdatePresentation presentation
) : IAutoUpdaterSingletonService
{
private bool _manualUpdateShown;
private bool _updatePromptOpen;
private GithubRelease? _availableRelease;
public bool IsUpdateAvailable => _availableRelease is not null;
public event EventHandler? UpdateAvailable;
private string CurrentVersion => brandingService.Branding.Version;

public async Task CheckForUpdatesAsync()
Expand All @@ -27,26 +34,44 @@ public async Task CheckForUpdatesAsync()
if (latestRelease?.TagName is null)
return;

var latestVersion = latestRelease.TagName.TrimStart('v');
if (!updatePlatform.SupportsAutomaticUpdate)
_availableRelease = latestRelease;
UpdateAvailable?.Invoke(this, EventArgs.Empty);
if (!updatePlatform.SupportsAutomaticUpdate && _manualUpdateShown)
return;

await ShowAvailableUpdateAsync();
}

public async Task ShowAvailableUpdateAsync()
{
if (_availableRelease is not { TagName: not null } latestRelease || _updatePromptOpen)
return;

_updatePromptOpen = true;
try
{
if (!_manualUpdateShown)
var latestVersion = latestRelease.TagName.TrimStart('v');
if (!updatePlatform.SupportsAutomaticUpdate)
{
_manualUpdateShown = true;
await presentation.ShowManualUpdateAsync(latestVersion, CurrentVersion);
return;
}
return;
}

var asset = updatePlatform.GetAssetForCurrentPlatform(latestRelease);
if (asset is null || !await presentation.ConfirmUpdateAsync(latestVersion, CurrentVersion))
return;
var asset = updatePlatform.GetAssetForCurrentPlatform(latestRelease);
if (asset is null || !await presentation.ConfirmUpdateAsync(latestVersion, CurrentVersion))
return;

var updateResult = await presentation.RunUpdateAsync(
(progress, cancellation) => updatePlatform.ExecuteUpdateAsync(asset.BrowserDownloadUrl, progress, cancellation)
);
if (updateResult.IsFailure)
await presentation.ShowUpdateFailureAsync(updateResult.Error.Message);
var updateResult = await presentation.RunUpdateAsync(
(progress, cancellation) => updatePlatform.ExecuteUpdateAsync(asset.BrowserDownloadUrl, progress, cancellation)
);
if (updateResult.IsFailure)
await presentation.ShowUpdateFailureAsync(updateResult.Error.Message);
}
finally
{
_updatePromptOpen = false;
}
}

private async Task<GithubRelease?> GetLatestReleaseAsync()
Expand Down
64 changes: 46 additions & 18 deletions WheelWizard/Views/Layout.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -290,24 +290,52 @@
VerticalAlignment="Center" />
</Border>

<Border Grid.Column="6" x:Name="VersionTagBorder"
Height="26"
BorderThickness="1"
BorderBrush="{StaticResource Neutral600}"
Background="Transparent"
CornerRadius="999"
HorizontalAlignment="Stretch"
VerticalAlignment="Center">
<TextBlock x:Name="VersionTagText"
Text="v0.0.0"
Classes="TinyText"
FontSize="13"
Foreground="{StaticResource Neutral300}"
FontWeight="Medium"
Margin="0,0,8,0"
HorizontalAlignment="Right"
VerticalAlignment="Center" />
</Border>
<Button Grid.Column="6" x:Name="VersionTagBorder"
Height="26" MaxWidth="90" ClipToBounds="True" HorizontalAlignment="Stretch" VerticalAlignment="Center"
Click="VersionTag_OnClick" AutomationProperties.Name="{loc:T action.update}">
<Button.Template>
<ControlTemplate>
<Border x:Name="BadgeBackground" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="999">
<ContentPresenter Content="{TemplateBinding Content}"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalAlignment="Center" Margin="8,0" />
</Border>
</ControlTemplate>
</Button.Template>
<Button.Styles>
<Style Selector="Button#VersionTagBorder">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="{StaticResource Neutral600}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Foreground" Value="{StaticResource Neutral300}" />
<Setter Property="Padding" Value="0" />
</Style>
<Style Selector="Button#VersionTagBorder.UpdateAvailable">
<Setter Property="Background" Value="{StaticResource Warning500}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Foreground" Value="{StaticResource Neutral950}" />
<Setter Property="Cursor" Value="Hand" />
</Style>
<Style Selector="Button#VersionTagBorder.UpdateAvailable:pointerover /template/ Border#BadgeBackground">
<Setter Property="Background" Value="{StaticResource Warning300}" />
</Style>
</Button.Styles>
<Grid ColumnDefinitions="Auto,*">
<Viewbox x:Name="VersionUpdateIcon" Width="16" Height="16" IsVisible="False"
HorizontalAlignment="Left" Margin="0,0,4,0">
<Canvas Width="24" Height="24">
<Path Data="M14 16a1 1 0 0 0 1-1v-2a1 1 0 0 1 1-1h3.293a.707.707 0 0 0 .5-1.207l-6.939-6.939a1.207 1.207 0 0 0-1.708 0l-6.94 6.94a.707.707 0 0 0 .5 1.206H8a1 1 0 0 1 1 1v2a1 1 0 0 0 1 1z M9 20h6"
Stroke="{Binding $parent[Button].Foreground}" StrokeThickness="2"
StrokeLineCap="Round" StrokeJoin="Round" />
</Canvas>
</Viewbox>
<TextBlock Grid.Column="1" x:Name="VersionTagText" Text="v0.0.0" Classes="TinyText"
HorizontalAlignment="Right"
FontSize="13" FontWeight="Medium" VerticalAlignment="Center" TextTrimming="CharacterEllipsis"
Foreground="{Binding $parent[Button].Foreground}" />
</Grid>
</Button>

<Grid.Styles>
<Style Selector="Border.BottomSidebarIcon">
Expand Down
22 changes: 21 additions & 1 deletion WheelWizard/Views/Layout.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Avalonia.Media;
using Avalonia.Threading;
using Avalonia.VisualTree;
using WheelWizard.AutoUpdating;
using WheelWizard.Branding;
using WheelWizard.Localization;
using WheelWizard.Mods;
Expand All @@ -29,6 +30,7 @@ namespace WheelWizard.Views;
public partial class Layout : BaseWindow, IPollingListener
{
private INavigationService Navigation { get; }
private readonly IAutoUpdaterSingletonService _autoUpdater;

protected override Control InteractionOverlay => DisabledDarkenEffect;
protected override Control InteractionContent => WindowFrame;
Expand Down Expand Up @@ -80,7 +82,8 @@ public Layout(
IGameLicenseSingletonService gameLicenseService,
ISettingsManager settingsService,
ISettingsSignalBus settingsSignalBus,
IModManager modManagerService
IModManager modManagerService,
IAutoUpdaterSingletonService autoUpdater
)
{
Navigation = navigation;
Expand All @@ -92,6 +95,9 @@ IModManager modManagerService
SettingsSignalBus = settingsSignalBus;
ModManagerService = modManagerService;
InitializeComponent();
_autoUpdater = autoUpdater;
_autoUpdater.UpdateAvailable += OnUpdateAvailable;
UpdateVersionBadge();

// Wayland does not expose the drawn caption buttons from our platform decoration template.
HeaderWindowControls.IsVisible =
Expand Down Expand Up @@ -159,6 +165,7 @@ await Task.WhenAll(

protected override void OnClosed(EventArgs e)
{
_autoUpdater.UpdateAvailable -= OnUpdateAvailable;
Navigation.PageChanged -= Navigation_OnPageChanged;
DetachLiveSubscriptions();
_settingsSignalSubscription?.Dispose();
Expand All @@ -168,6 +175,19 @@ protected override void OnClosed(EventArgs e)
base.OnClosed(e);
}

private void OnUpdateAvailable(object? sender, EventArgs e) => Dispatcher.UIThread.Post(UpdateVersionBadge);

private void UpdateVersionBadge()
{
var available = _autoUpdater.IsUpdateAvailable;
VersionTagBorder.Classes.Set("UpdateAvailable", available);
VersionTagBorder.IsHitTestVisible = available;
VersionTagBorder.Focusable = available;
VersionUpdateIcon.IsVisible = available;
}

private async void VersionTag_OnClick(object? sender, RoutedEventArgs e) => await _autoUpdater.ShowAvailableUpdateAsync();

private void OnLanguageChanged(object? sender, EventArgs e)
{
UpdateModsButtonText();
Expand Down
2 changes: 1 addition & 1 deletion WheelWizard/Views/Pages/HomePage.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@
Width="137" Angle="45" Y="-40" X="170" ExtendedHeight="1000" IsVisible="False"
Background="{StaticResource Primary600}" Foreground="{StaticResource Primary800}" />
<components:WheelTrail Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top" x:Name="HomeTrail3"
Width="137" Angle="45" Y="150" X="190" ExtendedHeight="1000" IsVisible="False"
Width="137" Angle="45" Y="165" X="175" ExtendedHeight="1000" IsVisible="False"
Background="{StaticResource Primary200}" Foreground="{StaticResource Primary600}" />

<Border Grid.Row="1" Height="630" Width="250" CornerRadius="125,125,0,0" VerticalAlignment="Bottom"
Expand Down
9 changes: 7 additions & 2 deletions WheelWizard/Views/Popups/Generic/MessageBoxWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@
<!-- Message Text -->
<ScrollViewer Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,10"
VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<SelectableTextBlock Classes="BodyText" x:Name="MessageInformationBlock" FontSize="14" TextWrapping="Wrap"
HorizontalAlignment="Left" Margin="0,10,0,0" />
<StackPanel>
<SelectableTextBlock Classes="BodyText" x:Name="MessageInformationBlock" FontSize="14" TextWrapping="Wrap"
HorizontalAlignment="Left" Margin="0,10,0,0" />
<components:IconLabelButton x:Name="MessageLink" IsVisible="False" IconSize="0" FontSize="14"
Foreground="{StaticResource Primary300}" HoverForeground="{StaticResource Primary200}"
HorizontalAlignment="Left" Click="MessageLink_OnClick" />
</StackPanel>
</ScrollViewer>

<!-- OK Button -->
Expand Down
16 changes: 16 additions & 0 deletions WheelWizard/Views/Popups/Generic/MessageBoxWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,22 @@ public MessageBoxWindow SetInfoText(string extraText)
return this;
}

public MessageBoxWindow SetWindowTitle(string title)
{
Window.WindowTitle = title;
return this;
}

public MessageBoxWindow SetLink(string url)
{
MessageLink.Text = url;
MessageLink.Tag = url;
MessageLink.IsVisible = true;
return this;
}

private void MessageLink_OnClick(object? sender, EventArgs e) => ViewUtils.OpenLink((string)MessageLink.Tag!);

public MessageBoxWindow SetTag(string extraText)
{
MessageTag.Text = extraText;
Expand Down
7 changes: 5 additions & 2 deletions WheelWizard/Views/Updating/UpdatePresentation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,13 @@ await Dispatcher.UIThread.InvokeAsync(
() =>
new MessageBoxWindow()
.SetTitleText("New Wheel Wizard version")
.SetWindowTitle("Update available")
.SetTag($"v{latestVersion}")
.SetInfoText(
$"There is a new Wheel Wizard version available!\nVersion {latestVersion} (You are currently on {currentVersion})\n"
+ "You can manually update it by going to the GitHub releases at: https://github.com/TeamWheelWizard/WheelWizard/releases"
$"There is a new Wheel Wizard version available!\nVersion {latestVersion} (You are currently on {currentVersion})\n\n"
+ "You can manually update it by going to the GitHub releases at:"
)
.SetLink("https://github.com/TeamWheelWizard/WheelWizard/releases")
.Show()
);

Expand Down
Loading