diff options
| author | Bobby <[email protected]> | 2025-07-18 12:24:06 +0530 |
|---|---|---|
| committer | Bobby <[email protected]> | 2025-07-18 12:24:06 +0530 |
| commit | 01e730c68a79862112798d4816625ddcd00350d9 (patch) | |
| tree | 3850e69c55334756b0faef76f54d9a74091f92d2 | |
| parent | 8df8cdd7e1bdefded59d073c14aa74666740be8c (diff) | |
| download | imageboard-01e730c68a79862112798d4816625ddcd00350d9.tar.xz imageboard-01e730c68a79862112798d4816625ddcd00350d9.zip | |
refactor ratings, minify content, update single post page
| -rw-r--r-- | config/enums.go | 2 | ||||
| -rw-r--r-- | go.mod | 2 | ||||
| -rw-r--r-- | go.sum | 5 | ||||
| -rw-r--r-- | imageboard/main.go | 2 | ||||
| -rw-r--r-- | middleware/middleware.go | 1 | ||||
| -rw-r--r-- | middleware/minifier.go | 79 | ||||
| -rw-r--r-- | router/routes.go | 2 | ||||
| -rw-r--r-- | static/css/main.css | 68 | ||||
| -rw-r--r-- | static/scripts/resize.js | 80 | ||||
| -rw-r--r-- | static/scripts/upload.js | 5 | ||||
| -rw-r--r-- | templates/partials/search.django | 8 | ||||
| -rw-r--r-- | templates/posts/single.django | 58 | ||||
| -rw-r--r-- | utils/handlers/req_map.go | 10 |
13 files changed, 307 insertions, 15 deletions
diff --git a/config/enums.go b/config/enums.go index e825d61..b6282e1 100644 --- a/config/enums.go +++ b/config/enums.go @@ -51,8 +51,8 @@ type Rating string const ( RatingSafe Rating = "Safe" - RatingQuestionable Rating = "Questionable" RatingSensitive Rating = "Sensitive" + RatingQuestionable Rating = "Questionable" RatingExplicit Rating = "Explicit" ) @@ -41,6 +41,8 @@ require ( github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/xid v1.6.0 // indirect + github.com/tdewolff/minify/v2 v2.23.8 // indirect + github.com/tdewolff/parse/v2 v2.8.1 // indirect github.com/tinylib/msgp v1.3.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.51.0 // indirect @@ -76,6 +76,11 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tdewolff/minify/v2 v2.23.8 h1:tvjHzRer46kwOfpdCBCWsDblCw3QtnLJRd61pTVkyZ8= +github.com/tdewolff/minify/v2 v2.23.8/go.mod h1:VW3ISUd3gDOZuQ/jwZr4sCzsuX+Qvsx87FDMjk6Rvno= +github.com/tdewolff/parse/v2 v2.8.1 h1:J5GSHru6o3jF1uLlEKVXkDxxcVx6yzOlIVIotK4w2po= +github.com/tdewolff/parse/v2 v2.8.1/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo= +github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= diff --git a/imageboard/main.go b/imageboard/main.go index 8e6fef5..511269b 100644 --- a/imageboard/main.go +++ b/imageboard/main.go @@ -42,8 +42,6 @@ func main() { processors.Initialize(app)
middleware.Initialize(app)
- app.Static("/static", "./static")
-
router.Initialize(app)
log.Fatalf("Server failed to start: %v", app.Listen(fmt.Sprintf("%s:%d", config.Server.Host, config.Server.Port)))
diff --git a/middleware/middleware.go b/middleware/middleware.go index ee51965..171e107 100644 --- a/middleware/middleware.go +++ b/middleware/middleware.go @@ -4,4 +4,5 @@ import "github.com/gofiber/fiber/v2" func Initialize(app *fiber.App) { app.Use(JSON) + app.Use(Minifier) } diff --git a/middleware/minifier.go b/middleware/minifier.go new file mode 100644 index 0000000..09d8813 --- /dev/null +++ b/middleware/minifier.go @@ -0,0 +1,79 @@ +package middleware + +import ( + "bytes" + "regexp" + + "github.com/gofiber/fiber/v2" + "github.com/tdewolff/minify/v2" + "github.com/tdewolff/minify/v2/css" + "github.com/tdewolff/minify/v2/html" + "github.com/tdewolff/minify/v2/js" + "github.com/tdewolff/minify/v2/json" + "github.com/tdewolff/minify/v2/xml" +) + +func Minifier(context *fiber.Ctx) error { + var ( + minifyHTML = true + minifyCSS = true + minifyJS = true + minifyXML = true + minifyJSON = true + ) + + m := minify.New() + + if minifyHTML { + m.Add("text/html", &html.Minifier{ + KeepEndTags: true, + KeepDocumentTags: true, + }) + } + + if minifyCSS { + m.Add("text/css", &css.Minifier{}) + } + + if minifyJS { + m.Add("application/javascript", &js.Minifier{}) + m.Add("application/x-javascript", &js.Minifier{}) + m.Add("text/javascript", &js.Minifier{}) + m.AddRegexp(regexp.MustCompile("^(application|text)/(x-)?(java|ecma)script$"), &js.Minifier{}) + } + + if minifyXML { + m.AddRegexp(regexp.MustCompile("xml$"), &xml.Minifier{}) + } + + if minifyJSON { + m.AddRegexp(regexp.MustCompile("json$"), &json.Minifier{}) + } + + if err := context.Next(); err != nil { + return err + } + + statusCode := context.Response().StatusCode() + if statusCode != fiber.StatusOK && statusCode != fiber.StatusNotModified { + return nil + } + + if statusCode == fiber.StatusNotModified { + return nil + } + origBody := context.Response().Body() + if len(origBody) == 0 { + return nil + } + + context.Response().ResetBody() + + mimetype := string(context.Response().Header.Peek("Content-Type")) + + if err := m.Minify(mimetype, context.Response().BodyWriter(), bytes.NewReader(origBody)); err != nil { + context.Response().BodyWriter().Write(origBody) + } + + return nil +} diff --git a/router/routes.go b/router/routes.go index bac5237..786e592 100644 --- a/router/routes.go +++ b/router/routes.go @@ -7,6 +7,8 @@ import ( )
func Initialize(router *fiber.App) {
+ router.Static("/static", "./static")
+
main := router.Group("/")
main.Get("/", controllers.HomePageController)
diff --git a/static/css/main.css b/static/css/main.css index f253f7c..018ca2a 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -89,6 +89,7 @@ main { border: 1px solid #4d4d80; padding: 8px; height: fit-content; + flex-shrink: 0; } .sidebar h3 { @@ -128,6 +129,7 @@ main { } .content { + overflow: hidden; flex: 1; background-color: #0a0015; border: 1px solid #4d4d80; @@ -795,4 +797,70 @@ footer::before { .post-tags .post-tag:hover { background-color: rgba(255, 255, 255, 0.2); transform: scale(1.1); +} + +.single-post { + width: 100%; + max-width: 100%; + overflow: hidden; + min-width: 0; +} + +.post-image-container { + width: 100%; + max-width: 100%; + overflow: auto; +} + +.post-image-container img { + display: block; + max-width: none; + height: auto; +} + +.post-title { + color: #ff99cc; +} + +.single-post-bar { + display: flex; + margin-bottom: 12px; + width: 100%; + padding: 8px; + border: 1px solid #4d4d80; + background-color: #0d001a; + flex-direction: row; + justify-content: space-between; +} + +.size-selected { + color: #ff99cc; + font-weight: 700; +} + +#post-image { + display: block; + height: auto; +} + +#post-image.fit-width { + width: 100%; + height: auto; + max-width: 100%; +} + +#post-image.fit-height { + width: auto; + max-width: none; +} + +#post-image.fit-both { + max-width: 100%; + height: auto; +} + +#post-image.fixed-size { + width: auto; + height: auto; + max-width: none; }
\ No newline at end of file diff --git a/static/scripts/resize.js b/static/scripts/resize.js new file mode 100644 index 0000000..95b75c1 --- /dev/null +++ b/static/scripts/resize.js @@ -0,0 +1,80 @@ +function getAvailableHeight() { + const navbar = document.querySelector('nav') + const postBar = document.querySelector('.single-post-bar') + const navbarHeight = navbar ? navbar.offsetHeight : 0 + const postBarHeight = postBar ? postBar.offsetHeight : 0 + const padding = 40 + return window.innerHeight - navbarHeight - postBarHeight - padding +} + +function updateSizeSelection(selectedSize) { + document.querySelectorAll('.single-post-bar-size-actions a').forEach((link) => { + link.classList.remove('size-selected') + if (link.onclick && link.onclick.toString().includes(selectedSize)) { + link.classList.add('size-selected') + } + }) +} + +let currentSize = 'fitBoth' + +function handleResize() { + if (currentSize === 'fitHeight' || currentSize === 'fitBoth') { + switchSize(currentSize) + } +} + +function switchSize(size) { + currentSize = size + const img = document.getElementById('post-image') + const container = document.querySelector('.post-image-container') + const sizeData = sizes[size] + + img.className = '' + img.style.width = '' + img.style.height = '' + img.style.maxWidth = '' + img.style.maxHeight = '' + + if (size === 'fitHeight') { + const availableHeight = getAvailableHeight() + img.style.maxHeight = availableHeight + 'px' + img.style.width = 'auto' + img.style.maxWidth = 'none' + img.classList.add('fit-height') + } else if (size === 'fitWidth') { + img.style.width = '100%' + img.style.height = 'auto' + img.style.maxWidth = '100%' + img.classList.add('fit-width') + } else if (size === 'fitBoth') { + const availableHeight = getAvailableHeight() + const imageWidth = parseInt(sizeData.width || img.naturalWidth) + const imageHeight = parseInt(sizeData.height || img.naturalHeight) + + if (imageHeight > imageWidth) { + img.style.maxHeight = availableHeight + 'px' + img.style.width = 'auto' + img.style.maxWidth = 'none' + } else { + img.style.width = '100%' + img.style.height = 'auto' + img.style.maxWidth = '100%' + } + img.classList.add('fit-both') + } else { + img.style.maxWidth = 'none' + if (sizeData.width) img.style.width = sizeData.width + 'px' + if (sizeData.height) img.style.height = sizeData.height + 'px' + img.classList.add('fixed-size') + } + + img.src = sizeData.src + updateSizeSelection(size) +} + +window.addEventListener('resize', handleResize) + +window.addEventListener('load', function () { + switchSize(currentSize) +})
\ No newline at end of file diff --git a/static/scripts/upload.js b/static/scripts/upload.js index fe4c4ef..0a6cc17 100644 --- a/static/scripts/upload.js +++ b/static/scripts/upload.js @@ -77,7 +77,7 @@ function createPreviewElement(key, blob, type, nameOrUrl) { const previewRatingForm = document.createElement('form'); previewRatingForm.className = 'preview-rating-form'; - ['Safe', 'Questionable', 'Sensitive', 'Explicit'].forEach((rating, idx) => { + ['Safe', 'Sensitive', 'Questionable', 'Explicit'].forEach((rating, idx) => { const inputId = `rating-${rating.toLowerCase()}-${key}`; const input = document.createElement('input'); input.type = 'radio'; @@ -625,6 +625,7 @@ function showImageProgress(previewElement) { height: 100%; width: 0%; background-color: #4a9eff; + transition: width 0.3s ease; `; progressContainer.appendChild(progressBar); @@ -663,7 +664,7 @@ function animateProgress(progressBar, duration) { if (isCompleted) return; const elapsed = Date.now() - startTime; - const timeRatio = elapsed / (duration * 4); + const timeRatio = elapsed / duration; let targetProgress; if (timeRatio < 0.2) { diff --git a/templates/partials/search.django b/templates/partials/search.django index 4798717..86a85b8 100644 --- a/templates/partials/search.django +++ b/templates/partials/search.django @@ -10,14 +10,14 @@ <span class="checkbox-custom safe"></span> </label> <label class="rating-checkbox"> - <input type="checkbox" name="rating" value="questionable" {{ QueryRatings.Questionable|yesno:'checked,' }} /> - <span class="checkbox-custom questionable"></span> - </label> - <label class="rating-checkbox"> <input type="checkbox" name="rating" value="sensitive" {{ QueryRatings.Sensitive|yesno:'checked,' }} /> <span class="checkbox-custom sensitive"></span> </label> <label class="rating-checkbox"> + <input type="checkbox" name="rating" value="questionable" {{ QueryRatings.Questionable|yesno:'checked,' }} /> + <span class="checkbox-custom questionable"></span> + </label> + <label class="rating-checkbox"> <input type="checkbox" name="rating" value="explicit" {{ QueryRatings.Explicit|yesno:'checked,' }} /> <span class="checkbox-custom explicit"></span> </label> diff --git a/templates/posts/single.django b/templates/posts/single.django index 4769309..8657ffe 100644 --- a/templates/posts/single.django +++ b/templates/posts/single.django @@ -5,6 +5,62 @@ <div class="error">{{ Error }}</div> </div> {% else %} - Single Post + <div class="single-post"> + <div class="single-post-bar"> + {% if Post.Title %} + <h1 class="post-title">{{ Post.Title }}</h1> + {% else %} + <h1 class="post-title">Post #{{ Post.ID }}</h1> + {% endif %} + <div class="single-post-bar-size-actions"> + <a href="javascript:void(0);" onclick="switchSize('fitBoth');" class="size-selected">Fit-Both</a> + <a href="javascript:void(0);" onclick="switchSize('fitHeight');">Fit-Height</a> + <a href="javascript:void(0);" onclick="switchSize('fitWidth');">Fit-Width</a> + <a href="javascript:void(0);" onclick="switchSize('small');">Small</a> + <a href="javascript:void(0);" onclick="switchSize('medium');">Medium</a> + <a href="javascript:void(0);" onclick="switchSize('large');">Large</a> + <a href="javascript:void(0);" onclick="switchSize('original');">Original</a> + </div> + </div> + <div class="post-image-container"> + <img src="{{ CDNURL }}/medium/{{ Post.FileName }}" alt="{{ Post.Title }}" id="post-image" /> + </div> + </div> {% endif %} {% endblock %} +{% block scripts %} + <script type="text/javascript"> + const sizes = { + fitBoth: { + src: '{{ CDNURL }}/medium/{{ Post.FileName }}' + }, + fitWidth: { + src: '{{ CDNURL }}/medium/{{ Post.FileName }}' + }, + fitHeight: { + src: '{{ CDNURL }}/medium/{{ Post.FileName }}' + }, + small: { + src: '{{ CDNURL }}/small/{{ Post.FileName }}', + width: '{{ Post.Sizes.2.Width }}', + height: '{{ Post.Sizes.2.Height }}' + }, + medium: { + src: '{{ CDNURL }}/medium/{{ Post.FileName }}', + width: '{{ Post.Sizes.3.Width }}', + height: '{{ Post.Sizes.3.Height }}' + }, + large: { + src: '{{ CDNURL }}/large/{{ Post.FileName }}', + width: '{{ Post.Sizes.4.Width }}', + height: '{{ Post.Sizes.4.Height }}' + }, + original: { + src: '{{ CDNURL }}/original/{{ Post.FileName }}', + width: '{{ Post.Sizes.5.Width }}', + height: '{{ Post.Sizes.5.Height }}' + } + } + </script> + <script type="text/javascript" src="/static/scripts/resize.js"></script> +{% endblock %} diff --git a/utils/handlers/req_map.go b/utils/handlers/req_map.go index 2b1145c..700b00a 100644 --- a/utils/handlers/req_map.go +++ b/utils/handlers/req_map.go @@ -14,12 +14,12 @@ func ExtractRatingsAndMap(queryParams []config.QueryParam) ([]config.Rating, map case "safe": ratings = append(ratings, config.RatingSafe) ratingsMap["Safe"] = true - case "questionable": - ratings = append(ratings, config.RatingQuestionable) - ratingsMap["Questionable"] = true case "sensitive": ratings = append(ratings, config.RatingSensitive) ratingsMap["Sensitive"] = true + case "questionable": + ratings = append(ratings, config.RatingQuestionable) + ratingsMap["Questionable"] = true case "explicit": ratings = append(ratings, config.RatingExplicit) ratingsMap["Explicit"] = true @@ -29,12 +29,12 @@ func ExtractRatingsAndMap(queryParams []config.QueryParam) ([]config.Rating, map if len(ratings) == 0 { ratings = []config.Rating{ config.RatingSafe, - config.RatingQuestionable, config.RatingSensitive, + config.RatingQuestionable, } ratingsMap["Safe"] = true - ratingsMap["Questionable"] = true ratingsMap["Sensitive"] = true + ratingsMap["Questionable"] = true } return ratings, ratingsMap } |
