From c97589bd1ae1d2366c4fb070264d26c8b8d8b7c5 Mon Sep 17 00:00:00 2001 From: Bobby Date: Sat, 12 Jul 2025 20:01:55 +0530 Subject: add cookie based preferences --- controllers/preferences.go | 2 +- imageboard/main.go | 2 +- middleware/middleware.go | 7 ++++ processors/preferences.go | 76 ++++++++++++++++++++++++++++++++++++ processors/processors.go | 1 + router/routes.go | 1 + static/css/main.css | 28 ++++++++----- static/scripts/preferences.js | 72 ++++++++++++++++++++++++++++++++++ static/scripts/theme.js | 37 ------------------ templates/layouts/main.django | 5 +++ templates/preferences.django | 91 ++++++++++++++++++++++++++++++------------- 11 files changed, 248 insertions(+), 74 deletions(-) create mode 100644 middleware/middleware.go create mode 100644 processors/preferences.go create mode 100644 static/scripts/preferences.js delete mode 100644 static/scripts/theme.js diff --git a/controllers/preferences.go b/controllers/preferences.go index 86e0fb3..042e810 100644 --- a/controllers/preferences.go +++ b/controllers/preferences.go @@ -6,7 +6,7 @@ import ( "github.com/gofiber/fiber/v2" ) -func PreferencesController(ctx *fiber.Ctx) error { +func PreferencesPageController(ctx *fiber.Ctx) error { ctx.Locals("Title", "Site Preferences") return shortcuts.Render(ctx, "preferences", nil) } diff --git a/imageboard/main.go b/imageboard/main.go index 178fed2..ba7836b 100644 --- a/imageboard/main.go +++ b/imageboard/main.go @@ -40,8 +40,8 @@ func main() { app.Use(cors.New()) processors.Initialize(app) + middleware.Initialize(app) - app.Use(middleware.JSON) app.Static("/static", "./static") router.Initialize(app) diff --git a/middleware/middleware.go b/middleware/middleware.go new file mode 100644 index 0000000..ee51965 --- /dev/null +++ b/middleware/middleware.go @@ -0,0 +1,7 @@ +package middleware + +import "github.com/gofiber/fiber/v2" + +func Initialize(app *fiber.App) { + app.Use(JSON) +} diff --git a/processors/preferences.go b/processors/preferences.go new file mode 100644 index 0000000..a96d1cb --- /dev/null +++ b/processors/preferences.go @@ -0,0 +1,76 @@ +package processors + +import ( + "encoding/json" + "fmt" + + "github.com/gofiber/fiber/v2" +) + +type SitePreferences struct { + SidebarWidth string `json:"sidebar_width"` + MainContentWidth string `json:"main_content_width"` + H1FontSize string `json:"h1_font_size"` + BodyFontSize string `json:"body_font_size"` + SmallFontSize string `json:"small_font_size"` + PostsPerPage int `json:"posts_per_page"` +} + +func PreferencesContextProcessor(context *fiber.Ctx) error { + defaultPreferences := SitePreferences{ + SidebarWidth: "180px", + MainContentWidth: "1200px", + H1FontSize: "16px", + BodyFontSize: "13px", + SmallFontSize: "11px", + PostsPerPage: 42, + } + + preferences := defaultPreferences + + preferencesCookie := context.Cookies("preferences") + if preferencesCookie != "" { + _ = json.Unmarshal([]byte(preferencesCookie), &preferences) + } + + bytes, err := json.Marshal(preferences) + if err == nil { + context.Cookie(&fiber.Cookie{ + Name: "preferences", + Value: string(bytes), + Path: "/", + SameSite: fiber.CookieSameSiteLaxMode, + }) + } + + context.Locals("Preferences", preferences) + context.Locals("PreferencesCSS", preferencesToCSS(preferences)) + return context.Next() +} + +func preferencesToCSS(preferences SitePreferences) string { + return fmt.Sprintf(` + `, + preferences.MainContentWidth, + preferences.BodyFontSize, + preferences.H1FontSize, + preferences.SmallFontSize, + preferences.SidebarWidth, + ) +} diff --git a/processors/processors.go b/processors/processors.go index e060abe..bbd23fe 100644 --- a/processors/processors.go +++ b/processors/processors.go @@ -6,4 +6,5 @@ func Initialize(app *fiber.App) { app.Use(RequestContextProcessor) app.Use(MetaContextProcessor) app.Use(SidebarContextProcessor) + app.Use(PreferencesContextProcessor) } diff --git a/router/routes.go b/router/routes.go index 4541419..91b3816 100644 --- a/router/routes.go +++ b/router/routes.go @@ -11,6 +11,7 @@ func Initialize(router *fiber.App) { main.Get("/", controllers.HomePageController) main.Get("/login", controllers.LoginPageController) main.Get("/register", controllers.RegisterPageController) + main.Get("/preferences", controllers.PreferencesPageController) posts := router.Group("/posts") posts.Get("/", controllers.PostsController) diff --git a/static/css/main.css b/static/css/main.css index 89fee52..aa92e1f 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -7,7 +7,6 @@ body { padding: 0; box-sizing: border-box; font-family: "LXGW WenKai Mono TC", monospace; - font-size: 13px; } body { @@ -79,7 +78,6 @@ nav::before { main { display: flex; - width: 1200px; margin: 0 auto; gap: 10px; padding: 10px; @@ -87,7 +85,6 @@ main { } .sidebar { - width: 180px; background-color: #0d0020; border: 1px solid #4d4d80; padding: 8px; @@ -148,10 +145,14 @@ main { .centered-main h1 { color: #ffccff; - font-size: 16px; margin: 8px 0px; } +.content-main h1 { + color: #ff99cc; + margin-bottom: 8px; +} + .centered-main p { color: #99ffcc; } @@ -177,7 +178,8 @@ main { input[type="text"], input[type="email"], -input[type="password"] { +input[type="password"], +input[type="number"] { background-color: #1a0033; border: 1px solid #9999ff; color: #ccccff; @@ -187,7 +189,8 @@ input[type="password"] { input[type="text"]:focus, input[type="email"]:focus, -input[type="password"]:focus { +input[type="password"]:focus, +input[type="number"]:focus { border-color: #ff99cc; background-color: #260040; outline: none; @@ -298,8 +301,6 @@ footer::before { margin: 8px 0; } - - .ibform { background-color: #0d001a; border: 1px solid #ff99cc; @@ -328,7 +329,6 @@ footer::before { .fg-sub small { color: #ff99cc; - font-size: 11px; } .fg-main label { @@ -354,4 +354,14 @@ footer::before { .q-img { max-width: 768px; +} + +.error { + color: #ffccff; + background-color: #330000; + border: 1px solid #ff0000; + padding: 8px; + margin-bottom: 16px; + text-align: center; + display: none; } \ No newline at end of file diff --git a/static/scripts/preferences.js b/static/scripts/preferences.js new file mode 100644 index 0000000..d02e5cc --- /dev/null +++ b/static/scripts/preferences.js @@ -0,0 +1,72 @@ +function validateCSSFieldValue(value) { + const validCSSValuePattern = /^(auto|(\d+(\.\d+)?px|em|rem|%)|calc\(.+\))$/; + return validCSSValuePattern.test(value); +} + +function validateCSSFontSize(value) { + const validFontSizePattern = /^(small|medium|large|x-large|\d+(\.\d+)?(px|em|rem|%)?)$/; + return validFontSizePattern.test(value); +} + +function showError(message) { + const errorMessage = document.getElementById('error-message'); + errorMessage.textContent = message; + errorMessage.style.display = 'block'; +} + +function hideError() { + const errorMessage = document.getElementById('error-message'); + errorMessage.textContent = ''; + errorMessage.style.display = 'none'; +} + +function setPreferences() { + const preferences = { + sidebar_width: document.getElementById('sidebar-width').value, + main_content_width: document.getElementById('main-width').value, + h1_font_size: document.getElementById('h1-font-size').value, + body_font_size: document.getElementById('body-font-size').value, + small_font_size: document.getElementById('small-font-size').value, + posts_per_page: parseInt(document.getElementById('posts-per-page').value, 10) + } + + for (const key in preferences) { + if (preferences[key] === '') { + showError(`Please fill in the ${key.replace(/_/g, ' ')} field.`); + return; + } + + switch (key) { + case 'sidebar_width': + case 'main_content_width': + if (!validateCSSFieldValue(preferences[key])) { + showError(`Invalid value for ${key.replace(/_/g, ' ')}: ${preferences[key]}. Please enter a valid CSS value (e.g., '300px', '50%', 'auto').`); + return; + } + break; + case 'h1_font_size': + case 'body_font_size': + case 'small_font_size': + if (!validateCSSFontSize(preferences[key])) { + showError(`Invalid font size for ${key.replace(/_/g, ' ')}: ${preferences[key]}. Please enter a valid font size (e.g., 'small', 'medium', 'large', '16px', '1.2em').`); + return; + } + break; + case 'posts_per_page': + if (isNaN(preferences[key]) || preferences[key] <= 0) { + showError('Posts per page must be a positive integer.'); + return; + } + break; + } + } + + document.cookie = `preferences=${JSON.stringify(preferences)}; path=/; SameSite=Lax;` + window.location.reload() +} + +function resetPreferences() { + hideError(); + document.cookie = 'preferences=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; SameSite=Lax;' + window.location.reload() +} diff --git a/static/scripts/theme.js b/static/scripts/theme.js deleted file mode 100644 index 0ba6625..0000000 --- a/static/scripts/theme.js +++ /dev/null @@ -1,37 +0,0 @@ -document.addEventListener('DOMContentLoaded', function () { - const savedTheme = localStorage.getItem('theme') || 'light'; - document.documentElement.setAttribute('data-theme', savedTheme); - - const preferencesForm = document.getElementById('preferences-form'); - if (preferencesForm) { - const themeRadios = document.querySelectorAll('input[name="theme"]'); - themeRadios.forEach(radio => { - if (radio.value === savedTheme) { - radio.checked = true; - } - }); - - preferencesForm.addEventListener('submit', function (e) { - e.preventDefault(); - const selectedTheme = document.querySelector('input[name="theme"]:checked').value; - localStorage.setItem('theme', selectedTheme); - document.documentElement.setAttribute('data-theme', selectedTheme); - - let successMsg = document.querySelector('.success-message'); - if (successMsg) { - successMsg.remove(); - } - - const message = document.createElement('div'); - message.className = 'success-message'; - message.textContent = 'Preferences saved successfully!'; - preferencesForm.parentNode.insertBefore(message, preferencesForm); - - setTimeout(() => { - if (message.parentNode) { - message.remove(); - } - }, 3000); - }); - } -}); \ No newline at end of file diff --git a/templates/layouts/main.django b/templates/layouts/main.django index 488f9e4..8e776d3 100644 --- a/templates/layouts/main.django +++ b/templates/layouts/main.django @@ -4,6 +4,8 @@ {{ Title }} - {{ Appname }} + + {{ PreferencesCSS|safe }} {% include 'partials/navbar.django' %} @@ -22,4 +24,7 @@

© 2025 {{ Appname }}. All rights reserved.

+ {% block scripts %} + + {% endblock %} diff --git a/templates/preferences.django b/templates/preferences.django index 53fc2fc..6b5f0e3 100644 --- a/templates/preferences.django +++ b/templates/preferences.django @@ -1,33 +1,72 @@ {% extends 'layouts/main.django' %} {% block content %} -

Site Preferences

- -
-
- Theme Settings - -
- -
-
- - -
-
- - -
+
+

Site Preferences

+

Customize your experience on {{ Appname }}. These settings are stored in your cookies and will persist across sessions. However, they are not coupled with your account, so they don't apply to other devices or browsers.

+ +
+
+
+ +
+
+ + Set the width of the sidebar. Use CSS units like px, em, or %.
- -
- +
+
+ +
+
+ + Set the width of the main content area. Use CSS units like px, em, or %. +
-
-
- -

- Back to Posts -

+
+
+ +
+
+ + Set the font size for H1 elements. This will affect the main headings across the site. Use a valid CSS size (e.g., 16px, 1.5em, etc.). +
+
+
+
+ +
+
+ + Set the font size for body text. This will affect the readability of the content across the site. Use a valid CSS size (e.g., 13px, 1em, etc.). +
+
+
+
+ +
+
+ + Set the font size for small text elements. This will affect the visibility of smaller text across the site. Use a valid CSS size (e.g., 11px, 0.8em, etc.). +
+
+
+
+ +
+
+ + Set the number of posts displayed per page. This can help manage load times +
+
+
+ + +
+ + +{% endblock %} +{% block scripts %} + {% endblock %} -- cgit v1.2.3