Add URL history to tabs

This commit is contained in:
Stone_Red
2024-05-17 10:47:32 +02:00
parent c5c55c1164
commit 8d50d759c9
7 changed files with 116 additions and 83 deletions
@@ -30,7 +30,7 @@ internal class ChangeThemeCommand : ICommand
ThemeVariant theme = app.ActualThemeVariant;
app.RequestedThemeVariant = theme == ThemeVariant.Dark ? ThemeVariant.Light : ThemeVariant.Dark;
Settings.Current.DarkMode = app.RequestedThemeVariant == ThemeVariant.Dark;
GlobalState.ReloadContent(this);
GlobalState.InvokeThemeChanged();
}
}
}
+1 -1
View File
@@ -21,6 +21,6 @@ internal class HomeCommand : ICommand
public void Execute(object? parameter)
{
GlobalState.Url = Settings.Current.HomeUrl ?? string.Empty;
GlobalState.CurrentTabState.Url = Settings.Current.HomeUrl ?? string.Empty;
}
}
+2 -2
View File
@@ -26,11 +26,11 @@ public class HyperlinkCommand : ICommand
if (Uri.IsWellFormedUriString(url, UriKind.Absolute))
{
GlobalState.Url = url;
GlobalState.CurrentTabState.Url = url;
}
else if (Uri.IsWellFormedUriString(url, UriKind.Relative) || url.StartsWith('/'))
{
GlobalState.Url = new Uri(new Uri(GlobalState.Url), url).ToString();
GlobalState.CurrentTabState.Url = new Uri(new Uri(GlobalState.CurrentTabState.Url), url).ToString();
}
else
{
+41
View File
@@ -0,0 +1,41 @@
using Markdowser.ViewModels;
using System;
using System.Collections.Generic;
namespace Markdowser.Models;
public class TabState
{
public event EventHandler? UrlChanged;
private string url = string.Empty;
public Stack<string> BackHistory { get; } = new();
public Stack<string> ForwardHistory { get; } = new();
public string Url
{
get => url;
set
{
if (url != value)
{
BackHistory.Push(url);
ForwardHistory.Clear();
}
url = value;
UrlChanged?.Invoke(this, EventArgs.Empty);
}
}
public ContentViewModelBase? Content { get; set; }
public void SetUrl(string url, object? sender = null)
{
this.url = url;
UrlChanged?.Invoke(sender ?? this, EventArgs.Empty);
}
}
+6 -40
View File
@@ -1,22 +1,16 @@
using Avalonia.Controls;
using CuteUtils.Logging;
using CuteUtils.Logging;
using Markdowser.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
namespace Markdowser.Utilities;
[SuppressMessage("Major Code Smell", "S4220:Events should have proper arguments", Justification = "<Pending>")]
internal static class GlobalState
{
public static event EventHandler<string>? UrlChanged;
public static event EventHandler? ThemeChanged;
public static event EventHandler? ContentReload;
private static string url = string.Empty;
public static TabState CurrentTabState { get; internal set; } = new();
public static Logger Logger { get; } = new()
{
@@ -54,36 +48,8 @@ internal static class GlobalState
}
};
public static Stack<string> BackHistory { get; } = new();
public static Stack<string> ForwardHistory { get; } = new();
public static string Url
public static void InvokeThemeChanged()
{
get => url;
set
{
if (url != value)
{
BackHistory.Push(url);
ForwardHistory.Clear();
}
url = value;
UrlChanged?.Invoke(null, url);
}
}
public static ObservableCollection<TabItem> Tabs { get; internal set; } = [new TabItem() { Header = "New Tab" }];
internal static void SetUrl(object sender, string url)
{
GlobalState.url = url;
UrlChanged?.Invoke(sender, url);
}
internal static void ReloadContent(object sender)
{
ContentReload?.Invoke(sender, EventArgs.Empty);
ThemeChanged?.Invoke(null, EventArgs.Empty);
}
}
+1 -1
View File
@@ -36,7 +36,7 @@ public class HttpPathResolver : IPathResolver
if (!Uri.IsWellFormedUriString(relativeOrAbsolutePath, UriKind.Absolute))
{
relativeOrAbsolutePath = new Uri(new Uri(GlobalState.Url), relativeOrAbsolutePath).ToString();
relativeOrAbsolutePath = new Uri(new Uri(GlobalState.CurrentTabState.Url), relativeOrAbsolutePath).ToString();
}
GlobalState.Logger.LogDebug($"Resolving image: {relativeOrAbsolutePath}");
@@ -27,32 +27,50 @@ public partial class MainWindowViewModel : ViewModelBase
private readonly ContentProcessorManager contentProcessorManager = new();
private readonly CacheService cacheService = new CacheService();
private ContentViewModelBase content;
private TabItem currentTab = null!;
private WindowState windowState;
private bool showSidePanel;
private bool isBusy;
private int progress;
public ObservableCollection<TabItem> Tabs => GlobalState.Tabs;
public ObservableCollection<TabItem> Tabs { get; } = [new TabItem() { Header = "New Tab" }];
public TabItem CurrentTab
{
get => currentTab;
set
{
if (currentTab is not null)
{
currentTab.Tag = Url;
}
CurrentTabState.UrlChanged -= UrlChanged;
_ = this.RaiseAndSetIfChanged(ref currentTab!, value);
this.RaisePropertyChanged(nameof(CurrentTabState));
this.RaisePropertyChanged(nameof(Url));
this.RaisePropertyChanged(nameof(BackEnabled));
this.RaisePropertyChanged(nameof(ForwardEnabled));
Url = value?.Tag?.ToString() ?? string.Empty;
GlobalState.CurrentTabState = CurrentTabState;
CurrentTabState.UrlChanged += UrlChanged;
FetchUrl(true);
}
}
public TabState CurrentTabState
{
get
{
if (CurrentTab is null)
{
return new();
}
return Dispatcher.UIThread.Invoke(() =>
{
CurrentTab.Tag ??= new TabState();
return (TabState)CurrentTab.Tag;
});
}
}
public WindowState WindowState
{
get => windowState;
@@ -61,14 +79,18 @@ public partial class MainWindowViewModel : ViewModelBase
public ContentViewModelBase Content
{
get => content;
set => this.RaiseAndSetIfChanged(ref content, value);
get => CurrentTabState.Content!;
set
{
CurrentTabState.Content = value;
this.RaisePropertyChanged();
}
}
public string Url
{
get => GlobalState.Url;
set => GlobalState.SetUrl(this, value);
get => CurrentTabState.Url;
set => CurrentTabState.SetUrl(value, this);
}
public bool ShowSidePanel
@@ -97,18 +119,18 @@ public partial class MainWindowViewModel : ViewModelBase
public RawHtmlViewModel RawHtmlViewModel => new RawHtmlViewModel(new());
public RawMarkdownViewModel RawMarkdownViewModel => new RawMarkdownViewModel(() => new());
public bool CloseTabEnabled => Tabs.Count > 1;
public bool BackEnabled => GlobalState.BackHistory.Count > 0;
public bool ForwardEnabled => GlobalState.ForwardHistory.Count > 0;
public bool BackEnabled => CurrentTabState.BackHistory.Count > 0;
public bool ForwardEnabled => CurrentTabState.ForwardHistory.Count > 0;
public bool ProgressIndeterminate => Progress == 0;
public ICommand Browse => ReactiveCommand.Create(() => FetchUrl(true));
public ICommand Reload => ReactiveCommand.Create(() => FetchUrl(false));
public ICommand Back => ReactiveCommand.Create(() =>
{
if (GlobalState.BackHistory.Count > 0)
if (CurrentTabState.BackHistory.Count > 0)
{
GlobalState.ForwardHistory.Push(GlobalState.Url);
Url = GlobalState.BackHistory.Pop();
CurrentTabState.ForwardHistory.Push(CurrentTabState.Url);
Url = CurrentTabState.BackHistory.Pop();
FetchUrl();
this.RaisePropertyChanged(nameof(BackEnabled));
@@ -118,10 +140,10 @@ public partial class MainWindowViewModel : ViewModelBase
public ICommand Forward => ReactiveCommand.Create(() =>
{
if (GlobalState.ForwardHistory.Count > 0)
if (CurrentTabState.ForwardHistory.Count > 0)
{
GlobalState.BackHistory.Push(GlobalState.Url);
Url = GlobalState.ForwardHistory.Pop();
CurrentTabState.BackHistory.Push(CurrentTabState.Url);
Url = CurrentTabState.ForwardHistory.Pop();
FetchUrl();
this.RaisePropertyChanged(nameof(ForwardEnabled));
@@ -178,30 +200,14 @@ public partial class MainWindowViewModel : ViewModelBase
{
httpClient.Timeout = TimeSpan.FromSeconds(10);
content = DefaultContent;
Tabs[0].Tag = new TabState() { Content = DefaultContent };
contentProcessorManager.RegisterProcessor(new HtmlProcessor());
contentProcessorManager.RegisterProcessor(new CommonImageProcessor());
GlobalState.UrlChanged += (sender, url) =>
GlobalState.ThemeChanged += (s, e) =>
{
this.RaisePropertyChanged(nameof(ForwardEnabled));
this.RaisePropertyChanged(nameof(BackEnabled));
this.RaisePropertyChanged(nameof(Url));
if (sender == this)
{
return;
}
FetchUrl();
};
GlobalState.ContentReload += (sender, _) =>
{
// Update icon when dark mode changes
this.RaisePropertyChanged(nameof(Icon));
this.RaisePropertyChanged(nameof(Content));
};
}
@@ -272,10 +278,10 @@ public partial class MainWindowViewModel : ViewModelBase
HttpResponseMessage httpResponseMessage = await httpClient.GetAsync(Url);
string? newUrl = httpResponseMessage.RequestMessage?.RequestUri?.ToString();
string oldUrl = Url;
if (newUrl is not null && newUrl != Url)
{
string oldUrl = Url;
Dispatcher.UIThread.Post(() => WindowNotificationManager.Show(new Notification("Redirected", $"Redirected from\n{oldUrl}\nto\n{newUrl}", NotificationType.Warning)));
Url = newUrl;
}
@@ -294,6 +300,12 @@ public partial class MainWindowViewModel : ViewModelBase
}
Content = await contentProcessorManager.ProcessContent(httpResponseMessage, new Progress<ProcessingProgress>(p => Progress = p.Percentage));
if (oldUrl != Url)
{
cacheService.Set(oldUrl, Content);
}
cacheService.Set(Url, Content);
}
@@ -340,4 +352,18 @@ public partial class MainWindowViewModel : ViewModelBase
}
});
}
private void UrlChanged(object? sender, EventArgs e)
{
this.RaisePropertyChanged(nameof(ForwardEnabled));
this.RaisePropertyChanged(nameof(BackEnabled));
this.RaisePropertyChanged(nameof(Url));
if (sender == this)
{
return;
}
FetchUrl();
}
}