diff --git a/Resources/Raw/whats-new.html b/Resources/Raw/whats-new.html
index 278c952..77b41ac 100644
--- a/Resources/Raw/whats-new.html
+++ b/Resources/Raw/whats-new.html
@@ -9,6 +9,14 @@
margin-top: 0!important;
}
+
Version 1.4.7 - January 6th, 2024
+ Happy new year!
+
+ - #42 Inlined image previews will now correctly open the source image when clicked.
+ - #44 Fixed relative link parsing in Opal 1.7.6 and updated Rosy Crow to use that version. This specifically affects links with relative URLs which lack a leading forward-slash.
+ - When prompting the user for input (for 1X responses), empty strings were being discarded. I changed this so that empty query parameters are sent correctly.
+ - I fixed an issue which prevented activating an identity when another identity was already active.
+
Version 1.4.6 - December 26th, 2023
#38 Error logs will now be correctly exported when that feature is used, rather than an empty file.
Version 1.4.5 - December 3rd, 2023
diff --git a/RosyCrow.csproj b/RosyCrow.csproj
index 08c5f53..2fd2f98 100644
--- a/RosyCrow.csproj
+++ b/RosyCrow.csproj
@@ -92,14 +92,14 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/Services/Cache/DiskCacheService.cs b/Services/Cache/DiskCacheService.cs
index ac8351d..a839b7e 100644
--- a/Services/Cache/DiskCacheService.cs
+++ b/Services/Cache/DiskCacheService.cs
@@ -26,11 +26,11 @@ public DiskCacheService(ILogger logger)
_logger.LogInformation(@"{Count} cache buckets pruned upon initialization", prunedCount);
}
- public async Task TryRead(Uri uri, Stream destination)
+ public async Task TryRead(Uri uri, Stream destination, bool isImage)
{
try
{
- if (!TryFindCachedByUri(uri, out var path))
+ if (!TryFindCachedByUri(uri, isImage, out var path))
return false;
_logger.LogDebug(@"Cached resource found at {Path}", path);
@@ -52,11 +52,11 @@ public async Task TryRead(Uri uri, Stream destination)
}
}
- public async Task Write(Uri uri, Stream contents)
+ public async Task Write(Uri uri, Stream contents, bool isImage)
{
try
{
- var path = GetCurrentPathFromUri(uri);
+ var path = GetCurrentPathFromUri(uri, isImage);
_logger.LogDebug(@"Cached page file path is {Path}", path);
@@ -114,15 +114,15 @@ private static string GetRootPath()
return FileSystem.CacheDirectory;
}
- private bool TryFindCachedByUri(Uri uri, out string path)
+ private bool TryFindCachedByUri(Uri uri, bool isImage, out string path)
{
// happy path: resource exists in the current hourly bucket
- path = GetCurrentPathFromUri(uri);
+ path = GetCurrentPathFromUri(uri, isImage);
if (File.Exists(path))
return true;
- var key = ComputeUriCachePath(uri);
+ var key = ComputeUriCachePath(uri, isImage);
foreach (var bucket in Directory.GetDirectories(GetRootPath()))
{
path = Path.Combine(bucket, key);
@@ -130,7 +130,7 @@ private bool TryFindCachedByUri(Uri uri, out string path)
if (File.Exists(path))
{
// move this file into the current bucket so we find it more quickly next time
- var newPath = GetCurrentPathFromUri(uri);
+ var newPath = GetCurrentPathFromUri(uri, isImage);
var directory = Path.GetDirectoryName(newPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
@@ -151,9 +151,9 @@ private bool TryFindCachedByUri(Uri uri, out string path)
return false;
}
- private static string GetCurrentPathFromUri(Uri uri)
+ private static string GetCurrentPathFromUri(Uri uri, bool isImage)
{
- return Path.Combine(GetRootPath(), GetHourlyDirectoryName(), ComputeUriCachePath(uri));
+ return Path.Combine(GetRootPath(), GetHourlyDirectoryName(), ComputeUriCachePath(uri, isImage));
}
private static string ComputeCachePath(string bucket, string key)
@@ -163,9 +163,10 @@ private static string ComputeCachePath(string bucket, string key)
Convert.ToHexString(MD5.HashData(Encoding.Default.GetBytes(key)))[..12] + ".dat");
}
- private static string ComputeUriCachePath(Uri uri)
+ private static string ComputeUriCachePath(Uri uri, bool isImage)
{
- return ComputeCachePath(uri.Host.ToUpperInvariant(), uri.PathAndQuery);
+ var path = ComputeCachePath(uri.Host.ToUpperInvariant(), uri.PathAndQuery);
+ return isImage ? Path.Combine("images/", path) : path;
}
private static string GetHourlyDirectoryName()
diff --git a/Services/Cache/ICacheService.cs b/Services/Cache/ICacheService.cs
index b597e6a..3349064 100644
--- a/Services/Cache/ICacheService.cs
+++ b/Services/Cache/ICacheService.cs
@@ -2,6 +2,6 @@
public interface ICacheService
{
- Task TryRead(Uri uri, Stream destination);
- Task Write(Uri uri, Stream contents);
+ Task TryRead(Uri uri, Stream destination, bool isImage);
+ Task Write(Uri uri, Stream contents, bool isImage);
}
\ No newline at end of file
diff --git a/Services/Document/DocumentService.cs b/Services/Document/DocumentService.cs
index 8957d91..6b805e7 100644
--- a/Services/Document/DocumentService.cs
+++ b/Services/Document/DocumentService.cs
@@ -158,7 +158,7 @@ public async Task RenderGemtextAsHtml(GemtextResponse g
// cache the page prior to injecting the stylesheet
await using var pageBuffer = new MemoryStream(Encoding.UTF8.GetBytes(document.DocumentNode.OuterHtml));
- await _cache.Write(gemtext.Uri, pageBuffer);
+ await _cache.Write(gemtext.Uri, pageBuffer, false);
return new RenderedGemtextDocument { HtmlContents = document.DocumentNode.OuterHtml, Title = title };
@@ -246,7 +246,7 @@ private static async Task CreateInlinedImagePreview(Stream source,
using var downsized = image.Downsize(256.0f, true);
var output = new MemoryStream();
- await downsized.SaveAsync(output);
+ await downsized.SaveAsync(output, ImageFormat.Jpeg, 0.75f);
return output;
}
@@ -263,7 +263,7 @@ private async Task TryLoadCachedImage(Uri uri)
{
var image = new MemoryStream();
- if (await _cache.TryRead(uri, image))
+ if (await _cache.TryRead(uri, image, true))
{
_logger.LogDebug(@"Loaded cached image originally from {URI}", uri);
return CreateInlineImageDataUrl(image);
@@ -308,7 +308,7 @@ private async Task FetchAndCacheInlinedImage(Uri uri)
}
image.Seek(0, SeekOrigin.Begin);
- await _cache.Write(uri, image);
+ await _cache.Write(uri, image, true);
_logger.LogDebug(@"Loaded an inlined image from {URI} after {Attempt} attempt(s)", uri, i + 1);
diff --git a/Services/Identity/IdentityService.cs b/Services/Identity/IdentityService.cs
index fdd1bd1..62b1f63 100644
--- a/Services/Identity/IdentityService.cs
+++ b/Services/Identity/IdentityService.cs
@@ -131,7 +131,7 @@ public async Task Activate(Models.Identity identity)
return;
// the identity has already been loaded
- if (ActiveCertificate != null)
+ if (ActiveCertificate?.Thumbprint == identity.Hash)
return;
_unlockingIdentity = true;
diff --git a/Views/BrowserView.xaml.cs b/Views/BrowserView.xaml.cs
index 4a9f4b9..5ce3625 100644
--- a/Views/BrowserView.xaml.cs
+++ b/Views/BrowserView.xaml.cs
@@ -322,7 +322,7 @@ public async Task LoadPage(bool triggeredByRefresh = false, bool useCache = fals
{
var cached = new MemoryStream();
- if (await _cache.TryRead(_tab.Location, cached))
+ if (await _cache.TryRead(_tab.Location, cached, false))
{
_logger.LogInformation(@"Loading a cached copy of the page");
@@ -336,7 +336,7 @@ public async Task LoadPage(bool triggeredByRefresh = false, bool useCache = fals
}
}
- if (!string.IsNullOrWhiteSpace(_tab.Input))
+ if (_tab.Input != null)
{
_logger.LogInformation(@"User provided input ""{Input}""", _tab.Input);
_tab.Location = new UriBuilder(_tab.Location) { Query = _tab.Input }.Uri;
@@ -381,8 +381,8 @@ private async Task HandleTitanResponse(IGeminiResponse response,
_tab.Input = await _tab.ParentPage.DisplayPromptOnMainThread(Text.BrowserView_LoadPage_Input_Required,
inputRequired.Message);
- if (string.IsNullOrEmpty(_tab.Input))
- return ResponseAction.Finished; // if no user-input was provided, then we cannot continue
+ if (_tab.Input == null)
+ return ResponseAction.Finished; // if no user-input was provided, then we cannot continue (empty string is valid)
return ResponseAction.Retry;
}
@@ -431,12 +431,11 @@ private async Task HandleGeminiResponse(IGeminiResponse response
_tab.Input = await _tab.ParentPage.DisplayPromptOnMainThread(Text.BrowserView_LoadPage_Input_Required,
inputRequired.Message);
- ;
- if (string.IsNullOrEmpty(_tab.Input))
+ if (_tab.Input == null)
{
_settingsDatabase.LastVisitedUrl = response.Uri.ToString();
- return ResponseAction.Finished; // if no user-input was provided, then we cannot continue
+ return ResponseAction.Finished; // if no user-input was provided, then we cannot continue (empty string is valid)
}
return ResponseAction.Retry;