aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob L. Kreuze2022-11-20 13:29:29 -0500
committerJakob L. Kreuze2022-11-20 13:29:29 -0500
commit7092a1977d075001320470d81cc740d33409cc64 (patch)
treed61003453c681f8d732d73255e12b0916f3aa1ad
parentebeeb7518241bea945b6c372fe07fb20abf020f3 (diff)
[dynamic] Initial dynamically-loaded comment form
To curb constraints surrounding where and when dynamic content can appear, the comment form _embedded into the page for a blog post_ is dynamically loaded by some JavaScript code. This is the initial implementation of that. There are a few parts of this implementation that need to be improved: - Make it clear that the captcha is the only part that's being loaded by XHR. i.e., display the form with a "loading..." placeholder where the captcha image should be. - Have this triggered by the user, so the API doesn't get flooded with requests that aren't going to be completed. (The ratio of readers to commenters is likely to be very high.)
-rw-r--r--haunt/api.scm3
-rw-r--r--haunt/jakob/builder/blog.scm12
-rw-r--r--haunt/jakob/dynamic/capabilities/comment-form.scm88
-rw-r--r--haunt/jakob/dynamic/captcha.scm14
-rw-r--r--haunt/static/js/proof-of-work.js3
5 files changed, 71 insertions, 49 deletions
diff --git a/haunt/api.scm b/haunt/api.scm
index 0103400..1d275f8 100644
--- a/haunt/api.scm
+++ b/haunt/api.scm
@@ -41,7 +41,8 @@
(log-append! 'info (format #f "~a ~a (~a) (~a)" method endpoint args originating-ip)))
((match (cons (request-method request) endpoint)
(('GET "comment-form" _) get-comment-form)
- ('(GET "challenge") make-pow-challenge!)
+ ('(GET "challenge" "proof-of-work") make-pow-challenge!)
+ ('(GET "challenge" "captcha") make-captcha-challenge!)
;; ('(GET "comments") get-comments)
(('POST "comment") put-comment)
(('GET "gallery") get-gallery)
diff --git a/haunt/jakob/builder/blog.scm b/haunt/jakob/builder/blog.scm
index 531804b..ba8a5ee 100644
--- a/haunt/jakob/builder/blog.scm
+++ b/haunt/jakob/builder/blog.scm
@@ -21,6 +21,7 @@
#:use-module (haunt utils)
#:use-module (ice-9 format)
#:use-module (ice-9 match)
+ #:use-module (jakob dynamic capabilities comment-form)
#:use-module (jakob theme)
#:use-module (jakob utils)
#:use-module (jakob utils pagination)
@@ -72,8 +73,12 @@
(article ,(post-sxml post))
(section
(@ (id "webmention"))
- (h2 ,(hyperlink "https://indieweb.org/Webmention" "Webmentions")
- " for this Page")
+ (h2 "Comments for this page")
+ (ul (@ (id "webmention-container"))
+ ;; ,@(render-comment-view (fetch-webmentions (post-identifier post))))
+ ,@(render-comment-view (fetch-comments (post-identifier post))))
+ (div (@ (id "dynamic-comment-form-insert") (hidden #t))
+ ,(render-comment-form (post-identifier post) #:header-level 'h3))
(p "Click " ,(hyperlink (build-comment-url post) "here") " to write a comment on this post.")
(form
(@ (id "webmention-form")
@@ -84,9 +89,6 @@
", you can let me know the URL:")
(input (@ (name "source") (type "url")))
(input (@ (value "Send Webmention") (type "submit"))))
- (ul (@ (id "webmention-container"))
- ;; ,@(render-comment-view (fetch-webmentions (post-identifier post))))
- ,@(render-comment-view (fetch-comments (post-identifier post))))
,(script "section-folds.js")
;; ,(script "webmention.js")
)))
diff --git a/haunt/jakob/dynamic/capabilities/comment-form.scm b/haunt/jakob/dynamic/capabilities/comment-form.scm
index 6937d15..29e00ec 100644
--- a/haunt/jakob/dynamic/capabilities/comment-form.scm
+++ b/haunt/jakob/dynamic/capabilities/comment-form.scm
@@ -29,44 +29,49 @@
#:use-module (web request)
#:use-module (web response)
#:use-module (web uri)
- #:export (render-comment-form-success
+ #:export (render-comment-form
+
+ render-comment-form-success
render-comment-form-failure
get-comment-form))
-(define (render-comment-form slug)
- (let-values (((captcha-id captcha-image) (new-captcha!)))
- `((div (@ (id "comment-form"))
- (h1 "Comment form")
- (form (@ (id "comment-input") (action "/api/comment") (method "post"))
- (input (@ (type "text") (name "slug") (hidden #t) (value ,slug)))
- (input (@ (type "text") (name "captcha-id") (hidden #t) (value ,captcha-id)))
- (fieldset (@ (id "commenter-info"))
- (legend "Commenter Info")
- (label (@ (for "name")) "Name:")
- (input (@ (type "text") (id "name") (name "name") (required #t) (size 24)))
- (label (@ (for "email")) "Email (optional, used for Gravatar):")
- (input (@ (type "text") (id "email") (name "email") (size 24)))
- (label (@ (for "url")) "Webpage URL (optional):")
- (input (@ (type "text") (id "url") (name "url") (size 24))))
- (fieldset (@ (id "comment-content"))
- (legend "Comment")
- (label (@ (for "subject")) "Subject (optional):")
- (input (@ (type "text") (id "subject") (name "subject") (size 24)))
- (label (@ (for "comment")) "Comment :")
- (textarea (@ (id "coment") (name "comment") (rows 4) (cols 50))))
- (fieldset (@ (id "comment-captcha"))
- (legend "Captcha")
- (label (@ (for "captcha")) "Please evaluate the following definite integral:")
- (img (@ (src ,(format #f "data:image/jpeg;charset=utf-8;base64,~a"
- (base64-encode captcha-image)))))
- (input (@ (type "text") (id "captcha") (name "captcha") (size 24)))
- (button (@ (id "pow-trigger") (hidden #t))
- "Too hard? (Or unable to see the challenge?) Click here.")
- (input (@ (type "text") (id "captcha-alt") (name "captcha-alt") (hidden #t)))
- (input (@ (type "text") (id "captcha-alt-id") (name "captcha-alt-id") (hidden #t)))
- (input (@ (type "submit") (id "submit-form") (value "Submit")))))
- ,(script "proof-of-work.js")))))
+(define* (render-comment-form slug #:key (header-level 'h1) (captcha-id "") captcha-image)
+ `(div (@ (id "comment-form"))
+ (,header-level "Comment form")
+ (form (@ (id "comment-input") (action "/api/comment") (method "post"))
+ (input (@ (type "text") (name "slug") (hidden #t) (value ,slug)))
+ (input (@ (type "text") (id "captcha-id") (name "captcha-id") (hidden #t) (autocomplete "off") (value ,captcha-id)))
+ (fieldset (@ (id "commenter-info"))
+ (legend "Commenter Info")
+ (label (@ (for "name")) "Name:")
+ (input (@ (type "text") (id "name") (name "name") (required #t) (size 24)))
+ (label (@ (for "email")) "Email (optional, used for Gravatar):")
+ (input (@ (type "text") (id "email") (name "email") (size 24)))
+ (label (@ (for "url")) "Webpage URL (optional):")
+ (input (@ (type "text") (id "url") (name "url") (size 24))))
+ (fieldset (@ (id "comment-content"))
+ (legend "Comment")
+ (label (@ (for "subject")) "Subject (optional):")
+ (input (@ (type "text") (id "subject") (name "subject") (size 24)))
+ (label (@ (for "comment")) "Comment :")
+ (textarea (@ (id "coment") (name "comment") (rows 4) (cols 50))))
+ (fieldset (@ (id "comment-captcha"))
+ (legend "Captcha")
+ (label (@ (for "captcha")) "Please evaluate the following definite integral:")
+ (img (@ (id "captcha-image")
+ (src ,(if captcha-image
+ (format #f "data:image/jpeg;charset=utf-8;base64,~a"
+ (base64-encode captcha-image))
+ ""))))
+ (input (@ (type "text") (id "captcha") (name "captcha") (size 24)))
+ (button (@ (id "pow-trigger") (hidden #t))
+ "Too hard? (Or unable to see the challenge?) Click here.")
+ (input (@ (type "text") (id "captcha-alt") (name "captcha-alt") (hidden #t)))
+ (input (@ (type "text") (id "captcha-alt-id") (name "captcha-alt-id") (hidden #t)))
+ (input (@ (type "submit") (id "submit-form") (value "Submit")))))
+ ,(script "dynamic-comment-form.js")
+ ,(script "proof-of-work.js")))
(define (render-comment-form-success slug)
`((div (h1 "Success!")
@@ -77,8 +82,13 @@
"API endpoint handler for querying for the comments on a particular post
This is a wrapper around `get-comments-by-slug'."
- (let* ((path-encoded (uri-path (request-uri request)))
- (path (split-and-decode-uri-path path-encoded))
- (slug (last path)))
- (values '((content-type . (text/html)))
- (sxml->html-string (theme #:content (render-comment-form slug) #:title "Comment prompt")))))
+ (let-values (((captcha-id captcha-image) (new-captcha!)))
+ (let* ((path-encoded (uri-path (request-uri request)))
+ (path (split-and-decode-uri-path path-encoded))
+ (slug (last path))
+ (form (render-comment-form slug
+ #:captcha-id captcha-id
+ #:captcha-image captcha-image)))
+ (values '((content-type . (text/html)))
+ (sxml->html-string
+ (theme #:content form #:title "Comment prompt"))))))
diff --git a/haunt/jakob/dynamic/captcha.scm b/haunt/jakob/dynamic/captcha.scm
index 0f6e0ba..05426d1 100644
--- a/haunt/jakob/dynamic/captcha.scm
+++ b/haunt/jakob/dynamic/captcha.scm
@@ -44,9 +44,10 @@
new-captcha!
validate-captcha!
+ make-captcha-challenge!
- make-pow-challenge!
- validate-proof-of-work!))
+ validate-proof-of-work!
+ make-pow-challenge!))
(define-record-type <id-queue>
(make-id-queue mutex min-free-threshold free-ids allocated-ids)
@@ -239,6 +240,15 @@ internally-defined `time-to-live-seconds'."
(<= (abs (- solution (string->number user-answer)))
epsilon))))
+(define (make-captcha-challenge! request body)
+ "API endpoint handler for requesting a captcha challenge"
+ (let-values (((challenge-id image) (new-captcha!)))
+ (values '((content-type . (application/json)))
+ (scm->json-string
+ `((challenge-id . ,challenge-id)
+ (image . ,(format #f "data:image/jpeg;charset=utf-8;base64,~a"
+ (base64-encode image))))))))
+
(define pow-challenge-id-queue (make-queue 1024))
diff --git a/haunt/static/js/proof-of-work.js b/haunt/static/js/proof-of-work.js
index 1a57c2a..5dedb66 100644
--- a/haunt/static/js/proof-of-work.js
+++ b/haunt/static/js/proof-of-work.js
@@ -68,10 +68,9 @@ function makeRequest (method, url) {
});
}
-
function raceEndpoint() {
return new Promise(function (resolve, reject) {
- makeRequest("GET", "/api/challenge")
+ makeRequest("GET", "/api/challenge/proof-of-work")
.then(function (data) {
let challengeData = JSON.parse(data);
findPrefix(challengeData.hardness, challengeData.nonce)

© 2015 - 2026 Jakob L. Kreuze