diff --git a/.gitignore b/.gitignore
index 426d76d..4f9edbb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -396,3 +396,143 @@ FodyWeavers.xsd
# JetBrains Rider
*.sln.iml
+
+### Intellij ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# AWS User-specific
+.idea/**/aws.xml
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# SonarLint plugin
+.idea/sonarlint/
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### Intellij Patch ###
+# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
+
+# *.iml
+# modules.xml
+# .idea/misc.xml
+# *.ipr
+
+# Sonarlint plugin
+# https://plugins.jetbrains.com/plugin/7973-sonarlint
+.idea/**/sonarlint/
+
+# SonarQube Plugin
+# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
+.idea/**/sonarIssues.xml
+
+# Markdown Navigator plugin
+# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
+.idea/**/markdown-navigator.xml
+.idea/**/markdown-navigator-enh.xml
+.idea/**/markdown-navigator/
+
+# Cache file creation bug
+# See https://youtrack.jetbrains.com/issue/JBR-2257
+.idea/$CACHE_FILE$
+
+# CodeStream plugin
+# https://plugins.jetbrains.com/plugin/12206-codestream
+.idea/codestream.xml
+
+# Azure Toolkit for IntelliJ plugin
+# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
+.idea/**/azureSettings.xml
+
+### Rider ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+
+# AWS User-specific
+
+# Generated files
+
+# Sensitive or high-churn files
+
+# Gradle
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
diff --git a/src/.idea/.idea.Markdowser/.idea/.name b/src/.idea/.idea.Markdowser/.idea/.name
new file mode 100644
index 0000000..3442041
--- /dev/null
+++ b/src/.idea/.idea.Markdowser/.idea/.name
@@ -0,0 +1 @@
+Markdowser
\ No newline at end of file
diff --git a/src/.idea/.idea.Markdowser/.idea/indexLayout.xml b/src/.idea/.idea.Markdowser/.idea/indexLayout.xml
new file mode 100644
index 0000000..7b08163
--- /dev/null
+++ b/src/.idea/.idea.Markdowser/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.idea/.idea.Markdowser/.idea/projectSettingsUpdater.xml b/src/.idea/.idea.Markdowser/.idea/projectSettingsUpdater.xml
new file mode 100644
index 0000000..4bb9f4d
--- /dev/null
+++ b/src/.idea/.idea.Markdowser/.idea/projectSettingsUpdater.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.idea/.idea.Markdowser/.idea/vcs.xml b/src/.idea/.idea.Markdowser/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/src/.idea/.idea.Markdowser/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.idea/.idea.Markdowser/.idea/workspace.xml b/src/.idea/.idea.Markdowser/.idea/workspace.xml
new file mode 100644
index 0000000..760e254
--- /dev/null
+++ b/src/.idea/.idea.Markdowser/.idea/workspace.xml
@@ -0,0 +1,80 @@
+
+
+
+ Markdowser/Markdowser.csproj
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Markdowser/Utilities/CacheService.cs b/src/Markdowser/Utilities/CacheService.cs
new file mode 100644
index 0000000..69f255d
--- /dev/null
+++ b/src/Markdowser/Utilities/CacheService.cs
@@ -0,0 +1,27 @@
+using Markdowser.ViewModels;
+
+using System.Collections.Generic;
+
+namespace Markdowser.Utilities;
+
+public class CacheService
+{
+ private readonly Dictionary cache = [];
+
+ public ContentViewModelBase? Get(string url)
+ {
+ if (cache.TryGetValue(url, out ContentViewModelBase? value))
+ {
+ return value;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public void Set(string url, ContentViewModelBase content)
+ {
+ cache[url] = content;
+ }
+}
\ No newline at end of file
diff --git a/src/Markdowser/ViewModels/MainWindowViewModel.cs b/src/Markdowser/ViewModels/MainWindowViewModel.cs
index 437a0d0..00a53e6 100644
--- a/src/Markdowser/ViewModels/MainWindowViewModel.cs
+++ b/src/Markdowser/ViewModels/MainWindowViewModel.cs
@@ -25,6 +25,7 @@ public partial class MainWindowViewModel : ViewModelBase
{
private readonly HttpClient httpClient = new(new HttpClientHandler() { AllowAutoRedirect = true });
private readonly ContentProcessorManager contentProcessorManager = new();
+ private readonly CacheService cacheService = new CacheService();
private ContentViewModelBase content;
private TabItem currentTab = null!;
@@ -48,7 +49,7 @@ public partial class MainWindowViewModel : ViewModelBase
Url = value?.Tag?.ToString() ?? string.Empty;
- FetchUrl();
+ FetchUrl(true);
}
}
@@ -99,7 +100,8 @@ public partial class MainWindowViewModel : ViewModelBase
public bool BackEnabled => GlobalState.BackHistory.Count > 0;
public bool ForwardEnabled => GlobalState.ForwardHistory.Count > 0;
public bool ProgressIndeterminate => Progress == 0;
- public ICommand Browse => ReactiveCommand.Create(FetchUrl);
+ public ICommand Browse => ReactiveCommand.Create(() => FetchUrl(true));
+ public ICommand Reload => ReactiveCommand.Create(() => FetchUrl(false));
public ICommand Back => ReactiveCommand.Create(() =>
{
@@ -208,7 +210,7 @@ public partial class MainWindowViewModel : ViewModelBase
return Uri.TryCreate(uriString, UriKind.Absolute, out uri) && (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps);
}
- private void FetchUrl()
+ private void FetchUrl(bool useCache = true)
{
if (IsBusy)
{
@@ -260,32 +262,41 @@ public partial class MainWindowViewModel : ViewModelBase
try
{
- HttpResponseMessage httpResponseMessage = await httpClient.GetAsync(Url);
-
- string? newUrl = httpResponseMessage.RequestMessage?.RequestUri?.ToString();
-
- if (newUrl is not null && newUrl != Url)
+ ContentViewModelBase? cachedContent = cacheService.Get(Url);
+ if (cachedContent is not null && useCache)
{
- string oldUrl = Url;
- Dispatcher.UIThread.Post(() => WindowNotificationManager.Show(new Notification("Redirected", $"Redirected from\n{oldUrl}\nto\n{newUrl}", NotificationType.Warning)));
- Url = newUrl;
+ Content = cachedContent;
}
-
- if (!httpResponseMessage.IsSuccessStatusCode)
+ else
{
- IsBusy = false;
+ HttpResponseMessage httpResponseMessage = await httpClient.GetAsync(Url);
- StringBuilder errorMessage = new();
+ string? newUrl = httpResponseMessage.RequestMessage?.RequestUri?.ToString();
- _ = errorMessage.AppendLine($"# {(int)httpResponseMessage.StatusCode} {httpResponseMessage.StatusCode}");
- _ = errorMessage.AppendLine($"Failed to fetch URL: {httpResponseMessage.ReasonPhrase}");
+ 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;
+ }
- Content = new MarkdownContentViewModel("Error", errorMessage.ToString());
- return;
+ if (!httpResponseMessage.IsSuccessStatusCode)
+ {
+ IsBusy = false;
+
+ StringBuilder errorMessage = new();
+
+ _ = errorMessage.AppendLine($"# {(int)httpResponseMessage.StatusCode} {httpResponseMessage.StatusCode}");
+ _ = errorMessage.AppendLine($"Failed to fetch URL: {httpResponseMessage.ReasonPhrase}");
+
+ Content = new MarkdownContentViewModel("Error", errorMessage.ToString());
+ return;
+ }
+
+ Content = await contentProcessorManager.ProcessContent(httpResponseMessage, new Progress(p => Progress = p.Percentage));
+ cacheService.Set(Url, Content);
}
- Content = await contentProcessorManager.ProcessContent(httpResponseMessage, new Progress(p => Progress = p.Percentage));
-
Dispatcher.UIThread.Post(() => CurrentTab.Header = Content.Title);
Logger.LogInfo($"Fetched URL: {Url}");
diff --git a/src/Markdowser/Views/Content/CommonImageContentView.axaml b/src/Markdowser/Views/Content/CommonImageContentView.axaml
index 1426a1d..34c13f8 100644
--- a/src/Markdowser/Views/Content/CommonImageContentView.axaml
+++ b/src/Markdowser/Views/Content/CommonImageContentView.axaml
@@ -8,7 +8,8 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:md="https://github.com/whistyun/Markdown.Avalonia"
xmlns:ctxt="clr-namespace:ColorTextBlock.Avalonia;assembly=ColorTextBlock.Avalonia"
- x:DataType="vm:Content.CommonImageContentViewModel"
+ xmlns:content="clr-namespace:Markdowser.ViewModels.Content"
+ x:DataType="content:CommonImageContentViewModel"
x:Class="Markdowser.Views.Content.CommonImageContentView">
diff --git a/src/Markdowser/Views/Content/MarkdownContentView.axaml b/src/Markdowser/Views/Content/MarkdownContentView.axaml
index 8b01caa..3dfdc1d 100644
--- a/src/Markdowser/Views/Content/MarkdownContentView.axaml
+++ b/src/Markdowser/Views/Content/MarkdownContentView.axaml
@@ -8,7 +8,8 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:md="https://github.com/whistyun/Markdown.Avalonia"
xmlns:ctxt="clr-namespace:ColorTextBlock.Avalonia;assembly=ColorTextBlock.Avalonia"
- x:DataType="vm:Content.MarkdownContentViewModel"
+ xmlns:content="clr-namespace:Markdowser.ViewModels.Content"
+ x:DataType="content:MarkdownContentViewModel"
x:Class="Markdowser.Views.Content.MarkdownContentView">
diff --git a/src/Markdowser/Views/MainWindow.axaml b/src/Markdowser/Views/MainWindow.axaml
index a3dc6ae..c0ddd1f 100644
--- a/src/Markdowser/Views/MainWindow.axaml
+++ b/src/Markdowser/Views/MainWindow.axaml
@@ -31,8 +31,8 @@
-
-
+
+
@@ -104,7 +104,7 @@
-
+