diff --git a/src/PostIt/PostIt.Tests/BlogReaderTests.cs b/src/PostIt/PostIt.Tests/BlogReaderTests.cs index d8e340878..4ee5b101f 100644 --- a/src/PostIt/PostIt.Tests/BlogReaderTests.cs +++ b/src/PostIt/PostIt.Tests/BlogReaderTests.cs @@ -82,6 +82,15 @@ public class BlogReaderTests Assert.Equal(StatusSeverity.Warning, vm.ActionStatus.Severity); } + [Theory] + [InlineData("https://org.example.test/files/paul/clip.mp4", true)] + [InlineData("https://localhost/files/paul/clip.mp4", false)] + public void Video_links_only_open_externally_when_the_source_is_a_real_external_resource(string source, bool expected) + { + var vm = new VideoPageViewModel(new Uri(source), "video/mp4"); + Assert.Equal(expected, vm.CanOpenExternal); + } + [AvaloniaFact] public async Task Reader_hides_editor_and_wires_hyperlinks_to_the_application() { diff --git a/src/PostIt/PostIt/ViewModels/Blogs/VideoPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Blogs/VideoPageViewModel.cs index e8fc1e535..38bf4b1c8 100644 --- a/src/PostIt/PostIt/ViewModels/Blogs/VideoPageViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/Blogs/VideoPageViewModel.cs @@ -1,17 +1,27 @@ using System; +using CommunityToolkit.Mvvm.ComponentModel; using Yavsc.Abstract.Streaming; using Yavsc.Api.Client; namespace PostIt.ViewModels; -public sealed class VideoPageViewModel(Uri source, string mimeType) : ViewModelBase +public partial class VideoPageViewModel(Uri source, string mimeType) : ViewModelBase { public Uri Source { get; } = source; public string MimeType { get; } = mimeType; public PodcastApiClient? PodcastClient { get; init; } - public PodcastResponse? Podcast { get; init; } - public bool CanOpenExternal => Podcast is null; + public PodcastResponse? Podcast { get; set; } + + public bool CanOpenExternal => CanLaunchExternally(Source); public string Title => Podcast?.Title ?? Uri.UnescapeDataString(System.IO.Path.GetFileName(Source.AbsolutePath)); public override bool CanNavigateNext { get => false; protected set { } } public override bool CanNavigatePrevious { get => true; protected set { } } + + private static bool CanLaunchExternally(Uri source) + => source is { IsAbsoluteUri: true } + && (string.Equals(source.Scheme, "http", StringComparison.OrdinalIgnoreCase) + || string.Equals(source.Scheme, "https", StringComparison.OrdinalIgnoreCase)) + && !string.Equals(source.Host, "localhost", StringComparison.OrdinalIgnoreCase) + && !string.Equals(source.AbsolutePath, "/", StringComparison.OrdinalIgnoreCase) + && !string.Equals(source.Scheme, "about", StringComparison.OrdinalIgnoreCase); } diff --git a/src/PostIt/PostIt/Views/Blogs/VideoPage.axaml.cs b/src/PostIt/PostIt/Views/Blogs/VideoPage.axaml.cs index e8a87f9aa..3966f488c 100644 --- a/src/PostIt/PostIt/Views/Blogs/VideoPage.axaml.cs +++ b/src/PostIt/PostIt/Views/Blogs/VideoPage.axaml.cs @@ -172,15 +172,6 @@ public partial class VideoPage : ContentPage private void PodcastFailed(string message) => Dispatcher.UIThread.Post(() => PlaybackStatus.Text = message); - private static bool CanLaunchExternally(Uri source) - => source is { IsAbsoluteUri: true } - && (string.Equals(source.Scheme, "http", StringComparison.OrdinalIgnoreCase) - || string.Equals(source.Scheme, "https", StringComparison.OrdinalIgnoreCase)) - && !string.Equals(source.Host, "localhost", StringComparison.OrdinalIgnoreCase) - && !string.Equals(source.AbsolutePath, "/", StringComparison.OrdinalIgnoreCase) - && !source.AbsolutePath.Contains("/api/", StringComparison.OrdinalIgnoreCase) - && !string.Equals(source.Scheme, "about", StringComparison.OrdinalIgnoreCase); - private void Player_EncounteredError(object? sender, EventArgs e) => Dispatcher.UIThread.Post(() => { @@ -208,7 +199,7 @@ public partial class VideoPage : ContentPage return; try { - if (!CanLaunchExternally(vm.Source)) + if (!vm.CanOpenExternal) { PlaybackStatus.Text = "Ce flux interne ne peut pas ĂȘtre ouvert dans le navigateur."; return;