summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJake Mannens <jake@asger.xyz>2026-04-16 02:22:56 +1000
committerJake Mannens <jake@asger.xyz>2026-04-16 02:22:56 +1000
commitba86ba12732b3290eaa74936950a370966b41ac5 (patch)
treefcb1fb3df3a0edc1d0a27a5336c8cadaef593507
parentd59e751c5b7c23f0dce2a146b6b8ced80231a0cb (diff)
v0.10av0.10a
-rw-r--r--.editorconfig3
-rw-r--r--Pages/Component/MobileMenu.razor6
-rw-r--r--Pages/Component/TagSelectDialog.razor11
-rw-r--r--Pages/Component/Titlebar.razor9
-rw-r--r--Pages/Gallery.razor.css3
-rw-r--r--Pages/ViewMedia.razor27
-rw-r--r--Pages/ViewMedia.razor.css16
-rw-r--r--Program.cs8
-rw-r--r--Server.csproj4
-rw-r--r--Services/ConfigService.cs4
-rw-r--r--Services/MediaService.cs8
-rw-r--r--Todo.md6
-rw-r--r--wwwroot/js/keyboard.js3
-rw-r--r--wwwroot/styles/global.css4
14 files changed, 74 insertions, 38 deletions
diff --git a/.editorconfig b/.editorconfig
index 6d8f727..913b3e5 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -2,3 +2,6 @@
# CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
dotnet_diagnostic.CS8618.severity = silent
+
+# ASP0000: Do not call 'IServiceCollection.BuildServiceProvider' in 'ConfigureServices'
+dotnet_diagnostic.ASP0000.severity = silent
diff --git a/Pages/Component/MobileMenu.razor b/Pages/Component/MobileMenu.razor
index 6cb3281..49c45d5 100644
--- a/Pages/Component/MobileMenu.razor
+++ b/Pages/Component/MobileMenu.razor
@@ -14,12 +14,6 @@
<a href="javascript:logout();">Logout</a>
</div>
-<script suppress-error="BL9992">
- function hideMobileMenu() {
- document.getElementById('mobile-menu').classList.add('hidden');
- }
-</script>
-
@code {
protected override void OnInitialized() =>
navigationManager.LocationChanged += LocationChanged;
diff --git a/Pages/Component/TagSelectDialog.razor b/Pages/Component/TagSelectDialog.razor
index 20be31e..87065d7 100644
--- a/Pages/Component/TagSelectDialog.razor
+++ b/Pages/Component/TagSelectDialog.razor
@@ -7,7 +7,16 @@
<link rel="stylesheet" href="@(nameof(HyperBooru)).styles.css"/>
<Dialog Title=@(Title ?? "Select one or more tag(s)") @ref=dialog>
- <input type="text" placeholder="Search" @ref=queryInput @oninput=QueryInput @onkeypress=QueryKey value=@query/>
+ <input
+ type="text"
+ placeholder="Search"
+ autocorrect="off"
+ autocapitalize="off"
+ autocomplete="off"
+ @ref=queryInput
+ @oninput=QueryInput
+ @onkeypress=QueryKey
+ value=@query/>
<div class="tag-definitions">
@for(int i = 0; i < tagDefinitions.Count(); i++) {
if(!MatchesQuery(tagDefinitions[i].tagDefinition))
diff --git a/Pages/Component/Titlebar.razor b/Pages/Component/Titlebar.razor
index a0b9eec..48257b2 100644
--- a/Pages/Component/Titlebar.razor
+++ b/Pages/Component/Titlebar.razor
@@ -67,7 +67,14 @@
<div id="navbar">
<h2>Login</h2>
<form class="login" action="javascript:login();">
- <input id="username" placeholder="Username" type="text"/>
+ <input
+ id="username"
+ placeholder="Username"
+ type="text"
+ autocorrect="off"
+ autocapitalize="off"
+ autocomplete="off"
+ autofocus/>
<input id="password" placeholder="Password" type="password"/>
</form>
<a href="javascript:login();">Login</a>
diff --git a/Pages/Gallery.razor.css b/Pages/Gallery.razor.css
index 1b5ed86..6226d9b 100644
--- a/Pages/Gallery.razor.css
+++ b/Pages/Gallery.razor.css
@@ -1,6 +1,9 @@
img {
+ height: auto;
margin-right: 5px;
max-height: 200px;
+ max-width: 100%;
+ width: auto;
}
div#feed-error {
diff --git a/Pages/ViewMedia.razor b/Pages/ViewMedia.razor
index 27a366c..46cbc45 100644
--- a/Pages/ViewMedia.razor
+++ b/Pages/ViewMedia.razor
@@ -14,23 +14,34 @@
document.getElementById("hcontainer").classList.toggle("hide-metadata");
}
- function showSidebar(visible) {
- document.getElementById("hcontainer").classList.toggle("hide-metadata", !visible)
+ function setMobilePane(pane) {
+ var panes = Array.from(document.querySelectorAll('[class^="mobile-pane-"]'));
+
+ panes.forEach(e => e.classList.remove('visible'));
+ panes
+ .filter(e => e.classList.contains(`mobile-pane-${pane}`))
+ .forEach(e => e.classList.add('visible'));
+ }
+
+ function pageKeyDownHandler(e) {
+ if(!e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey && !e.isComposing)
+ if(e.key == 's')
+ toggleSidebar();
}
</script>
<div id="vcontainer">
- <div id="hcontainer" class="hide-metadata">
- <div id="image-container">
+ <div id="hcontainer">
+ <div id="image-container" class="mobile-pane-image visible">
<img
src="/media/@(media.Guid)"
width=@media.CurrentUploadedFile.Width
height=@media.CurrentUploadedFile.Height/>
</div>
<div id="metadata-show-button">
- <a href="javascript:toggleSidebar();"></a>
+ <a href="javascript:toggleSidebar();" title="Toggle sidebar (S)"></a>
</div>
- <div id="metadata">
+ <div id="metadata" class="mobile-pane-metadata">
<div id="metadata-container">
<div id="metadata-fileinfo">
@if(infoEditMode) {
@@ -143,8 +154,8 @@
</div>
</div>
<div id="bottom-bar">
- <img onclick="showSidebar(false);" src="/images/photo.svg" width="25" height="25"/>
- <img onclick="showSidebar(true);" src="/images/info.svg" width="25" height="25"/>
+ <img onclick="setMobilePane('image');" src="/images/photo.svg" width="25" height="25"/>
+ <img onclick="setMobilePane('metadata');" src="/images/info.svg" width="25" height="25"/>
</div>
</div>
diff --git a/Pages/ViewMedia.razor.css b/Pages/ViewMedia.razor.css
index 080994a..60b04be 100644
--- a/Pages/ViewMedia.razor.css
+++ b/Pages/ViewMedia.razor.css
@@ -100,20 +100,12 @@ div#metadata {
}
@media (hover: none) and (pointer: coarse) {
- div#image-container {
- display: none;
+ [class^="mobile-pane-"] {
+ width: 100% !important;
}
- div#metadata {
- width: 100%;
- }
-
- div#hcontainer.hide-metadata > div#image-container {
- display: initial;
- }
-
- div#hcontainer.hide-metadata > div#metadata {
- display: none;
+ [class^="mobile-pane-"]:not(.visible) {
+ display: none !important;
}
}
diff --git a/Program.cs b/Program.cs
index e7f1e26..548f6e9 100644
--- a/Program.cs
+++ b/Program.cs
@@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore;
using System.Text.Json.Serialization;
using HyperBooru.Services;
using Microsoft.AspNetCore.Authentication.Cookies;
+using Microsoft.AspNetCore.DataProtection;
namespace HyperBooru;
@@ -30,6 +31,13 @@ public class Program {
builder.Services.AddHostedService<OcrService>();
builder.Services.AddSingleton<ISourceService, SourceService>();
+ // Ensure session keys are stored in a persistent location on all platforms
+ builder.Services.AddDataProtection()
+ .PersistKeysToFileSystem(new(
+ builder.Services.BuildServiceProvider()
+ .GetRequiredService<IConfigService>()
+ .KeyPath));
+
var app = builder.Build();
// Ensure database is created and it's schema is up to date
diff --git a/Server.csproj b/Server.csproj
index 6e4af7d..f733581 100644
--- a/Server.csproj
+++ b/Server.csproj
@@ -6,9 +6,9 @@
<ImplicitUsings>enable</ImplicitUsings>
<AssemblyName>HyperBooru</AssemblyName>
<RootNamespace>HyperBooru</RootNamespace>
- <AssemblyVersion>0.9.0.0</AssemblyVersion>
+ <AssemblyVersion>0.10.0.0</AssemblyVersion>
<FileVersion>$(AssemblyVersion)</FileVersion>
- <Version>0.9-alpha</Version>
+ <Version>0.10-alpha</Version>
<UserSecretsId>2907567f-4640-4581-8f4d-0977952d26bd</UserSecretsId>
</PropertyGroup>
diff --git a/Services/ConfigService.cs b/Services/ConfigService.cs
index d2d1a06..752f9f5 100644
--- a/Services/ConfigService.cs
+++ b/Services/ConfigService.cs
@@ -2,6 +2,7 @@
public interface IConfigService {
public string DataPath { get; }
+ public string KeyPath { get; }
public string DbConnectionString { get; }
public string MediaBasePath { get; }
public string ThumbnailBasePath { get; }
@@ -38,6 +39,9 @@ public class ConfigService : IConfigService {
}
}
+ public string KeyPath =>
+ Path.Join(DataPath, "keys");
+
public string DbConnectionString =>
config.GetConnectionString("DefaultConnection") ??
throw new HBException("Unable to get default connection string");
diff --git a/Services/MediaService.cs b/Services/MediaService.cs
index 2f7eac6..27c77d6 100644
--- a/Services/MediaService.cs
+++ b/Services/MediaService.cs
@@ -285,10 +285,10 @@ public class MediaService : IMediaService {
using var image = new MagickImage(GetPath(mediaId));
- if(width > image.Width || height > image.Height)
- throw new ThumbnailException(
- "Requested thumbnail size is larger than original media",
- mediaId);
+ if(width > image.Width || height > image.Height) {
+ width = (int) image.Width;
+ height = (int) image.Height;
+ }
image.Thumbnail((uint) (width ?? -1), (uint) (height ?? -1));
image.Write(thumbPath, MagickFormat.Jpeg);
diff --git a/Todo.md b/Todo.md
index 2292aae..23c406d 100644
--- a/Todo.md
+++ b/Todo.md
@@ -1,4 +1,8 @@
# Bugs
+ - [X] Images in the gallery on mobile can easily exceed screen width
+ - [X] Images smaller than the requested thumbnail size aren't delivered
+ - [X] Autocorrect needs to be disabled on inputs such as username, tag name, etc
+ - [X] Mobile menu does not automatically hide upon page navigation
- [X] Input not focused
- [ ] Setting implicit tags removes builtin tags
- [X] UserService listeners don't seem to be removed after disposal
@@ -8,6 +12,8 @@
- [ ] Can't delete media
# Short-term Features
+ - [ ] Ability to set password (at least via API)
+ - [ ] PowerShell uploading with initial tagging
- [ ] Proper thumbnail generation
- [ ] Video support
- [ ] User/security support
diff --git a/wwwroot/js/keyboard.js b/wwwroot/js/keyboard.js
index 59eec4c..8b46639 100644
--- a/wwwroot/js/keyboard.js
+++ b/wwwroot/js/keyboard.js
@@ -49,6 +49,9 @@ async function keyDownHandler(e) {
e.preventDefault();
return;
}
+
+ if(typeof pageKeyDownHandler == 'function')
+ pageKeyDownHandler(e);
}
window.onload = () => document.onkeydown = keyDownHandler; \ No newline at end of file
diff --git a/wwwroot/styles/global.css b/wwwroot/styles/global.css
index 2bd24bc..9de9fc1 100644
--- a/wwwroot/styles/global.css
+++ b/wwwroot/styles/global.css
@@ -114,10 +114,6 @@ button > img {
}
@media (hover: none) and (pointer: coarse) {
- button {
- height: auto;
- }
-
button > :not(:first-child) {
display: none;
}