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
44 changes: 44 additions & 0 deletions src/Files.App/Actions/Open/OpenReleaseNotesAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

using CommunityToolkit.WinUI.Helpers;

namespace Files.App.Actions
{
internal sealed class OpenReleaseNotesAction : ObservableObject, IAction
{
private readonly IDialogService DialogService = Ioc.Default.GetRequiredService<IDialogService>();
private readonly IUpdateService UpdateService = Ioc.Default.GetRequiredService<IUpdateService>();
public string Label
=> Strings.WhatsNew.GetLocalizedResource();

public string Description
=> Strings.WhatsNewDescription.GetLocalizedResource();

public bool IsExecutable
=> UpdateService.AreReleaseNotesAvailable;

public OpenReleaseNotesAction()
{
UpdateService.PropertyChanged += UpdateService_PropertyChanged;
}

public Task ExecuteAsync(object? parameter = null)
{
var viewModel = new ReleaseNotesDialogViewModel(Constants.ExternalUrl.ReleaseNotesUrl);
var dialog = DialogService.GetDialog(viewModel);

return dialog.TryShowAsync();
}

private void UpdateService_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(IUpdateService.AreReleaseNotesAvailable):
OnPropertyChanged(nameof(IsExecutable));
break;
}
}
}
}
3 changes: 3 additions & 0 deletions src/Files.App/Constants.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

using CommunityToolkit.WinUI.Helpers;

namespace Files.App
{
public static class Constants
Expand Down Expand Up @@ -205,6 +207,7 @@ public static class ExternalUrl
public const string PrivacyPolicyUrl = @"https://files.community/privacy";
public const string SupportUsUrl = @"https://github.com/sponsors/yaira2";
public const string CrowdinUrl = @"https://crowdin.com/project/files-app";
public static readonly string ReleaseNotesUrl= $"https://files.community/blog/posts/v{SystemInformation.Instance.ApplicationVersion.Major}-{SystemInformation.Instance.ApplicationVersion.Minor}-{SystemInformation.Instance.ApplicationVersion.Build}?minimal";
}

public static class DocsPath
Expand Down
1 change: 1 addition & 0 deletions src/Files.App/Data/Commands/Manager/CommandCodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ public enum CommandCodes
OpenInVSCode,
OpenRepoInVSCode,
OpenProperties,
OpenReleaseNotes,
OpenClassicProperties,
OpenSettings,
OpenStorageSense,
Expand Down
2 changes: 2 additions & 0 deletions src/Files.App/Data/Commands/Manager/CommandManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public IRichCommand this[HotKey hotKey]
public IRichCommand OpenInVSCode => commands[CommandCodes.OpenInVSCode];
public IRichCommand OpenRepoInVSCode => commands[CommandCodes.OpenRepoInVSCode];
public IRichCommand OpenProperties => commands[CommandCodes.OpenProperties];
public IRichCommand OpenReleaseNotes => commands[CommandCodes.OpenReleaseNotes];
public IRichCommand OpenClassicProperties => commands[CommandCodes.OpenClassicProperties];
public IRichCommand OpenStorageSense => commands[CommandCodes.OpenStorageSense];
public IRichCommand OpenStorageSenseFromHome => commands[CommandCodes.OpenStorageSenseFromHome];
Expand Down Expand Up @@ -317,6 +318,7 @@ public IEnumerator<IRichCommand> GetEnumerator() =>
[CommandCodes.OpenInVSCode] = new OpenInVSCodeAction(),
[CommandCodes.OpenRepoInVSCode] = new OpenRepoInVSCodeAction(),
[CommandCodes.OpenProperties] = new OpenPropertiesAction(),
[CommandCodes.OpenReleaseNotes] = new OpenReleaseNotesAction(),
[CommandCodes.OpenClassicProperties] = new OpenClassicPropertiesAction(),
[CommandCodes.OpenStorageSense] = new OpenStorageSenseAction(),
[CommandCodes.OpenStorageSenseFromHome] = new OpenStorageSenseFromHomeAction(),
Expand Down
1 change: 1 addition & 0 deletions src/Files.App/Data/Commands/Manager/ICommandManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public interface ICommandManager : IEnumerable<IRichCommand>
IRichCommand OpenInVSCode { get; }
IRichCommand OpenRepoInVSCode { get; }
IRichCommand OpenProperties { get; }
IRichCommand OpenReleaseNotes { get; }
IRichCommand OpenClassicProperties { get; }
IRichCommand OpenStorageSense { get; }
IRichCommand OpenStorageSenseFromHome { get; }
Expand Down
13 changes: 4 additions & 9 deletions src/Files.App/Data/Contracts/IUpdateService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,22 @@ public interface IUpdateService : INotifyPropertyChanged
bool IsUpdating { get; }

/// <summary>
/// Gets a value indicating if the apps being used the first time after an update.
/// Gets a value indicating if the app is being used the first time after an update.
/// </summary>
bool IsAppUpdated { get; }

/// <summary>
/// Gets a value indicating if release notes are available.
/// Gets a value that indicates if there are release notes available for the current version of the app.
/// </summary>
bool IsReleaseNotesAvailable { get; }
bool AreReleaseNotesAvailable { get; }

Task DownloadUpdatesAsync();

Task DownloadMandatoryUpdatesAsync();

Task CheckForUpdatesAsync();

Task CheckLatestReleaseNotesAsync(CancellationToken cancellationToken = default);

/// <summary>
/// Gets release notes for the latest release
/// </summary>
Task<string?> GetLatestReleaseNotesAsync(CancellationToken cancellationToken = default);
Task CheckForReleaseNotesAsync();

/// <summary>
/// Replace Files.App.Launcher.exe if it is used and has been updated
Expand Down
22 changes: 8 additions & 14 deletions src/Files.App/Dialogs/ReleaseNotesDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<ContentDialog.Resources>
<ResourceDictionary>
<Thickness x:Key="ContentDialogPadding">0</Thickness>
<x:Double x:Key="ContentDialogMaxWidth">1100</x:Double>
</ResourceDictionary>
</ContentDialog.Resources>

Expand Down Expand Up @@ -60,21 +61,14 @@
</Button>
</Grid>

<!-- Markdown Content -->
<ScrollViewer
<!-- WebView -->
<WebView2
x:Name="BlogPostWebView"
Grid.Row="1"
MinWidth="440"
MinHeight="200"
Padding="20,0,20,20"
HorizontalScrollMode="Auto"
VerticalScrollMode="Auto">
<controls:MarkdownTextBlock
x:Name="ReleaseNotesMarkdownTextBlock"
Background="Transparent"
LinkClicked="ReleaseNotesMarkdownTextBlock_LinkClicked"
ListGutterWidth="16"
Text="{x:Bind ViewModel.ReleaseNotesMadrkdown, Mode=OneWay}" />
</ScrollViewer>
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CoreWebView2Initialized="BlogPostWebView_CoreWebView2Initialized"
Source="{x:Bind ViewModel.BlogPostUrl, Mode=OneWay}" />

<!-- Footer -->
<Border
Expand Down
43 changes: 39 additions & 4 deletions src/Files.App/Dialogs/ReleaseNotesDialog.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
using Windows.System;
using Windows.UI.WebUI;

namespace Files.App.Dialogs
{
Expand All @@ -28,7 +30,10 @@ public ReleaseNotesDialog()

private void UpdateDialogLayout()
{
ContainerGrid.MaxHeight = MainWindow.Instance.Bounds.Height - 70;
var maxHeight = MainWindow.Instance.Bounds.Height - 70;
var maxWidth = MainWindow.Instance.Bounds.Width;
ContainerGrid.Height = maxHeight > 740 ? 740 : maxHeight;
ContainerGrid.Width = maxWidth > 740 ? 740 : maxWidth;
}

private void Current_SizeChanged(object sender, WindowSizeChangedEventArgs e)
Expand Down Expand Up @@ -60,10 +65,40 @@ private ContentDialog SetContentDialogRoot(ContentDialog contentDialog)
return contentDialog;
}

private async void ReleaseNotesMarkdownTextBlock_LinkClicked(object sender, CommunityToolkit.WinUI.UI.Controls.LinkClickedEventArgs e)
private async void BlogPostWebView_CoreWebView2Initialized(WebView2 sender, CoreWebView2InitializedEventArgs args)
{
if (Uri.TryCreate(e.Link, UriKind.Absolute, out Uri? link))
await Launcher.LaunchUriAsync(link);
BlogPostWebView.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
BlogPostWebView.CoreWebView2.Settings.AreDevToolsEnabled = false;
BlogPostWebView.CoreWebView2.Settings.AreBrowserAcceleratorKeysEnabled = false;
BlogPostWebView.CoreWebView2.Settings.IsSwipeNavigationEnabled = false;

var script = @"
document.addEventListener('click', function(event) {
var target = event.target;
while (target && target.tagName !== 'A') {
target = target.parentElement;
}
if (target && target.href) {
event.preventDefault();
window.chrome.webview.postMessage(target.href);
}
});
";

await sender.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(script);
sender.WebMessageReceived += WebView_WebMessageReceived;
}

private async void WebView_WebMessageReceived(WebView2 sender, CoreWebView2WebMessageReceivedEventArgs args)
{
// Open link in web browser
if (Uri.TryCreate(args.TryGetWebMessageAsString(), UriKind.Absolute, out Uri? uri))
await Launcher.LaunchUriAsync(uri);

// Navigate back to blog post
if (sender.CoreWebView2.CanGoBack)
sender.CoreWebView2.GoBack();
}

}
}
2 changes: 1 addition & 1 deletion src/Files.App/Helpers/Application/AppLifecycleHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public static async Task CheckAppUpdate()
await updateService.CheckForUpdatesAsync();
await updateService.DownloadMandatoryUpdatesAsync();
await updateService.CheckAndUpdateFilesLauncherAsync();
await updateService.CheckLatestReleaseNotesAsync();
await updateService.CheckForReleaseNotesAsync();
}

/// <summary>
Expand Down
41 changes: 25 additions & 16 deletions src/Files.App/Services/App/AppUpdateNoneService.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CommunityToolkit.WinUI.Helpers;
using System.Net.Http;

namespace Files.App.Services
{
internal sealed class DummyUpdateService : IUpdateService
internal sealed class DummyUpdateService : ObservableObject, IUpdateService
{
public bool IsUpdateAvailable => false;

public bool IsUpdating => false;

public bool IsAppUpdated => true;
public bool IsAppUpdated
{
get => SystemInformation.Instance.IsAppUpdated;
}

public bool IsReleaseNotesAvailable => true;
private bool _areReleaseNotesAvailable = false;
public bool AreReleaseNotesAvailable
{
get => _areReleaseNotesAvailable;
private set => SetProperty(ref _areReleaseNotesAvailable, value);
}

public event PropertyChangedEventHandler? PropertyChanged { add { } remove { } }

Expand All @@ -28,9 +33,19 @@ public Task CheckForUpdatesAsync()
return Task.CompletedTask;
}

public Task CheckLatestReleaseNotesAsync(CancellationToken cancellationToken = default)
public async Task CheckForReleaseNotesAsync()
{
return Task.CompletedTask;
using var client = new HttpClient();

try
{
var response = await client.GetAsync(Constants.ExternalUrl.ReleaseNotesUrl);
AreReleaseNotesAvailable = response.IsSuccessStatusCode;
}
catch
{
AreReleaseNotesAvailable = false;
}
}

public Task DownloadMandatoryUpdatesAsync()
Expand All @@ -42,11 +57,5 @@ public Task DownloadUpdatesAsync()
{
return Task.CompletedTask;
}

public Task<string?> GetLatestReleaseNotesAsync(CancellationToken cancellationToken = default)
{
// No localization for dev-only string
return Task.FromResult((string?)"No release notes available for Dev build.");
}
}
}
52 changes: 20 additions & 32 deletions src/Files.App/Services/App/AppUpdateSideloadService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,13 @@ public bool IsAppUpdated
get => SystemInformation.Instance.IsAppUpdated;
}

private bool _isReleaseNotesAvailable;
public bool IsReleaseNotesAvailable
private bool _areReleaseNotesAvailable = false;
public bool AreReleaseNotesAvailable
{
get => _isReleaseNotesAvailable;
private set => SetProperty(ref _isReleaseNotesAvailable, value);
get => _areReleaseNotesAvailable;
private set => SetProperty(ref _areReleaseNotesAvailable, value);
}

public async Task DownloadUpdatesAsync()
{
await ApplyPackageUpdateAsync();
Expand All @@ -74,34 +75,6 @@ public Task DownloadMandatoryUpdatesAsync()
return Task.CompletedTask;
}

public async Task<string?> GetLatestReleaseNotesAsync(CancellationToken cancellationToken = default)
{
var applicationVersion = $"{SystemInformation.Instance.ApplicationVersion.Major}.{SystemInformation.Instance.ApplicationVersion.Minor}.{SystemInformation.Instance.ApplicationVersion.Build}";
var releaseNotesLocation = string.Concat("https://raw.githubusercontent.com/files-community/Release-Notes/main/", applicationVersion, ".md");

using var client = new HttpClient();

try
{
var result = await client.GetStringAsync(releaseNotesLocation, cancellationToken);
return result == string.Empty ? null : result;
}
catch
{
return null;
}
}

public async Task CheckLatestReleaseNotesAsync(CancellationToken cancellationToken = default)
{
if (!IsAppUpdated)
return;

var result = await GetLatestReleaseNotesAsync();
if (result is not null)
IsReleaseNotesAvailable = true;
}

public async Task CheckForUpdatesAsync()
{
IsUpdateAvailable = false;
Expand Down Expand Up @@ -191,6 +164,21 @@ bool HashEqual(Stream a, Stream b)
}
}

public async Task CheckForReleaseNotesAsync()
{
using var client = new HttpClient();

try
{
var response = await client.GetAsync(Constants.ExternalUrl.ReleaseNotesUrl);
AreReleaseNotesAvailable = response.IsSuccessStatusCode;
}
catch
{
AreReleaseNotesAvailable = false;
}
}

private async Task StartBackgroundDownloadAsync()
{
try
Expand Down
Loading
Loading