diff --git a/breezewiki.rkt b/breezewiki.rkt
index a8b8c28..44d6771 100644
--- a/breezewiki.rkt
+++ b/breezewiki.rkt
@@ -17,6 +17,7 @@
(require-reloadable "src/page-proxy.rkt" page-proxy)
(require-reloadable "src/page-redirect-wiki-home.rkt" redirect-wiki-home)
(require-reloadable "src/page-search.rkt" page-search)
+(require-reloadable "src/page-set-user-settings.rkt" page-set-user-settings)
(require-reloadable "src/page-static.rkt" static-dispatcher)
(require-reloadable "src/page-subdomain.rkt" subdomain-dispatcher)
(require-reloadable "src/page-wiki.rkt" page-wiki)
@@ -40,6 +41,7 @@
page-not-found
page-proxy
page-search
+ page-set-user-settings
page-wiki
page-file
redirect-wiki-home
diff --git a/dist.rkt b/dist.rkt
index 777e81a..35e1824 100644
--- a/dist.rkt
+++ b/dist.rkt
@@ -11,6 +11,7 @@
(require (only-in "src/page-proxy.rkt" page-proxy))
(require (only-in "src/page-redirect-wiki-home.rkt" redirect-wiki-home))
(require (only-in "src/page-search.rkt" page-search))
+(require (only-in "src/page-set-user-settings.rkt" page-set-user-settings))
(require (only-in "src/page-static.rkt" static-dispatcher))
(require (only-in "src/page-subdomain.rkt" subdomain-dispatcher))
(require (only-in "src/page-wiki.rkt" page-wiki))
@@ -29,6 +30,7 @@
page-not-found
page-proxy
page-search
+ page-set-user-settings
page-wiki
page-file
redirect-wiki-home
diff --git a/info.rkt b/info.rkt
index 46512df..dd34d49 100644
--- a/info.rkt
+++ b/info.rkt
@@ -1,3 +1,3 @@
#lang info
-(define build-deps '("rackunit-lib" "web-server-lib" "http-easy-lib" "html-parsing" "html-writing" "json-pointer" "ini-lib" "memo"))
+(define build-deps '("rackunit-lib" "web-server-lib" "http-easy-lib" "html-parsing" "html-writing" "json-pointer" "ini-lib" "memo" "net-cookies-lib"))
diff --git a/src/application-globals.rkt b/src/application-globals.rkt
index b139192..0fe26dc 100644
--- a/src/application-globals.rkt
+++ b/src/application-globals.rkt
@@ -1,8 +1,10 @@
#lang racket/base
-(require racket/list
+(require racket/file
+ racket/list
racket/string
json
(prefix-in easy: net/http-easy)
+ html-parsing
html-writing
web-server/http
"config.rkt"
@@ -34,6 +36,11 @@
(header #"Link" (string->bytes/latin-1 link-header))))
(define timeouts (easy:make-timeout-config #:lease 5 #:connect 5))
+(define theme-icons
+ (for/hasheq ([theme '(default light dark)])
+ (values theme
+ (html->xexp (file->string (format "static/icon-theme-~a.svg" theme) #:mode 'binary)))))
+
(define (application-footer source-url #:license [license-in #f])
(define license (or license-in license-default))
`(footer (@ (class "custom-footer"))
@@ -98,24 +105,27 @@
(define (generate-wiki-page
content
+ #:req req
#:source-url source-url
#:wikiname wikiname
#:title title
#:head-data [head-data-in #f]
- #:siteinfo [siteinfo-in #f])
+ #:siteinfo [siteinfo-in #f]
+ #:user-cookies [user-cookies-in #f])
(define siteinfo (or siteinfo-in siteinfo-default))
(define head-data (or head-data-in ((head-data-getter wikiname))))
+ (define user-cookies (or user-cookies-in (user-cookies-getter req)))
(define (required-styles origin)
(map (λ (dest-path)
(define url (format dest-path origin))
(if (config-true? 'strict_proxy)
(u-proxy-url url)
url))
- '(#;"~a/load.php?lang=en&modules=skin.fandomdesktop.styles&only=styles&skin=fandomdesktop"
+ `(#;"~a/load.php?lang=en&modules=skin.fandomdesktop.styles&only=styles&skin=fandomdesktop"
#;"~a/load.php?lang=en&modules=ext.gadget.dungeonsWiki%2CearthWiki%2Csite-styles%2Csound-styles&only=styles&skin=fandomdesktop"
#;"~a/load.php?lang=en&modules=site.styles&only=styles&skin=fandomdesktop"
; combine the above entries into a single request for potentially extra speed - fandom.com doesn't even do this!
- "~a/wikia.php?controller=ThemeApi&method=themeVariables"
+ ,(format "~~a/wikia.php?controller=ThemeApi&method=themeVariables&variant=~a" (user-cookies^-theme user-cookies))
"~a/load.php?lang=en&modules=skin.fandomdesktop.styles%7Cext.fandom.PortableInfoboxFandomDesktop.css%7Cext.fandom.GlobalComponents.CommunityHeaderBackground.css%7Cext.gadget.site-styles%2Csound-styles%7Csite.styles&only=styles&skin=fandomdesktop")))
`(*TOP*
(*DECL* DOCTYPE html)
@@ -155,7 +165,22 @@
(label (@ (for "bw-search-input")) "Search ")
(div (@ (id "bw-pr-search-input"))
(input (@ (type "text") (name "q") (id "bw-search-input") (autocomplete "off"))))
- (div (@ (class "bw-ss__container") (id "bw-pr-search-suggestions"))))))
+ (div (@ (class "bw-ss__container") (id "bw-pr-search-suggestions"))))
+ (div (@ (class "bw-theme__select"))
+ (span (@ (class "bw-theme__main-label")) "Page theme")
+ (div (@ (class "bw-theme__items"))
+ ,@(for/list ([theme '(default light dark)])
+ (define class
+ (if (equal? theme (user-cookies^-theme user-cookies))
+ "bw-theme__item bw-theme__item--selected"
+ "bw-theme__item"))
+ `(a (@ (href ,(user-cookies-setter-url
+ req
+ (struct-copy user-cookies^ user-cookies
+ [theme theme]))) (class ,class))
+ (div (@ (class "bw-theme__icon-container"))
+ ,(hash-ref theme-icons theme))
+ ,(format "~a" theme)))))))
(div (@ (id "content") #;(class "page-content"))
(div (@ (id "mw-content-text"))
,content))
@@ -179,11 +204,11 @@
page))))
"/proxy?dest=https%3A%2F%2Ftest.fandom.com")))
-(define (generate-redirect dest)
+(define (generate-redirect dest #:headers [headers-in '()])
(define dest-bytes (string->bytes/utf-8 dest))
(response/output
#:code 302
- #:headers (list (header #"Location" dest-bytes))
+ #:headers (append (list (header #"Location" dest-bytes)) headers-in)
(λ (out)
(write-html
`(html
diff --git a/src/data.rkt b/src/data.rkt
index f856710..e8a9e69 100644
--- a/src/data.rkt
+++ b/src/data.rkt
@@ -1,6 +1,9 @@
#lang racket/base
(require racket/list
racket/match
+ web-server/http/request-structs
+ net/url-string
+ (only-in net/cookies/server cookie-header->alist cookie->set-cookie-header make-cookie)
(prefix-in easy: net/http-easy)
memo
"static-data.rkt"
@@ -11,11 +14,16 @@
(struct-out siteinfo^)
(struct-out license^)
(struct-out head-data^)
+ (struct-out user-cookies^)
siteinfo-fetch
siteinfo-default
license-default
head-data-getter
- head-data-default)
+ head-data-default
+ user-cookies-getter
+ user-cookies-default
+ user-cookies-setter
+ user-cookies-setter-url)
(struct siteinfo^ (sitename basepage license) #:transparent)
(struct license^ (text url) #:transparent)
@@ -61,3 +69,27 @@
(set! this-data data))
;; then no matter what, return the best information we have so far
this-data))
+
+(struct user-cookies^ (theme) #:prefab)
+(define user-cookies-default (user-cookies^ 'default))
+(define (user-cookies-getter req)
+ (define cookie-header (headers-assq* #"cookie" (request-headers/raw req)))
+ (define cookies-alist (if cookie-header (cookie-header->alist (header-value cookie-header) bytes->string/utf-8) null))
+ (define cookies-hash
+ (for/hasheq ([pair cookies-alist])
+ (match pair
+ [(cons "theme" (and theme (or "light" "dark" "default")))
+ (values 'theme (string->symbol theme))]
+ [_ (values #f #f)])))
+ (user-cookies^
+ (hash-ref cookies-hash 'theme (user-cookies^-theme user-cookies-default))))
+
+(define (user-cookies-setter user-cookies)
+ (map (λ (c) (header #"Set-Cookie" (cookie->set-cookie-header c)))
+ (list (make-cookie "theme" (symbol->string (user-cookies^-theme user-cookies))
+ #:path "/"
+ #:max-age (* 60 60 24 365 10)))))
+
+(define (user-cookies-setter-url req new-settings)
+ (format "/set-user-settings?~a" (params->query `(("ref" . ,(url->string (request-uri req)))
+ ("new_settings" . ,(format "~a" new-settings))))))
diff --git a/src/dispatcher-tree.rkt b/src/dispatcher-tree.rkt
index 63c053d..251aa43 100644
--- a/src/dispatcher-tree.rkt
+++ b/src/dispatcher-tree.rkt
@@ -39,6 +39,7 @@
(pathprocedure:make "/" (hash-ref ds 'page-home))
(pathprocedure:make "/proxy" (hash-ref ds 'page-proxy))
(pathprocedure:make "/search" (hash-ref ds 'page-global-search))
+ (pathprocedure:make "/set-user-settings" (hash-ref ds 'page-set-user-settings))
(pathprocedure:make "/buddyfight/wiki/It_Doesn't_Work!!" (hash-ref ds 'page-it-works))
(filter:make (pregexp (format "^/~a/wiki/Category:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-category)))
(filter:make (pregexp (format "^/~a/wiki/File:.+$" px-wikiname)) (lift:make (hash-ref ds 'page-file)))
diff --git a/src/page-category.rkt b/src/page-category.rkt
index 69d9f42..5933f3e 100644
--- a/src/page-category.rkt
+++ b/src/page-category.rkt
@@ -29,6 +29,7 @@
'#hasheq((batchcomplete . #t) (continue . #hasheq((cmcontinue . "page|4150504c45|41473") (continue . "-||"))) (query . #hasheq((categorymembers . (#hasheq((ns . 0) (pageid . 25049) (title . "Item (entity)")) #hasheq((ns . 0) (pageid . 128911) (title . "3D")) #hasheq((ns . 0) (pageid . 124018) (title . "A Very Fine Item")) #hasheq((ns . 0) (pageid . 142208) (title . "Amethyst Shard")) #hasheq((ns . 0) (pageid . 121612) (title . "Ankle Monitor")))))))))
(define (generate-results-page
+ #:req req
#:source-url source-url
#:wikiname wikiname
#:title title
@@ -38,6 +39,7 @@
#:siteinfo [siteinfo #f])
(define members (jp "/query/categorymembers" members-data))
(generate-wiki-page
+ #:req req
#:source-url source-url
#:wikiname wikiname
#:title title
@@ -96,6 +98,7 @@
(define page (html->xexp page-html))
(define head-data ((head-data-getter wikiname) page-data))
(define body (generate-results-page
+ #:req req
#:source-url source-url
#:wikiname wikiname
#:title title
diff --git a/src/page-file.rkt b/src/page-file.rkt
index 1802568..d9dfd91 100644
--- a/src/page-file.rkt
+++ b/src/page-file.rkt
@@ -51,7 +51,8 @@
[(regexp-match? #rx"(?i:^video/)" content-type) `(video (@ (src ,maybe-proxied-url) (controls)))]
[else `""]))
-(define (generate-results-page #:source-url source-url
+(define (generate-results-page #:req req
+ #:source-url source-url
#:wikiname wikiname
#:title title
#:media-detail media-detail
@@ -68,6 +69,7 @@
(define maybe-proxied-raw-image-url
(if (config-true? 'strict_proxy) (u-proxy-url raw-image-url) raw-image-url))
(generate-wiki-page
+ #:req req
#:source-url source-url
#:wikiname wikiname
#:title title
@@ -125,7 +127,8 @@
#f
(url-content-type (jp "/imageUrl" media-detail))))
(define body
- (generate-results-page #:source-url source-url
+ (generate-results-page #:req req
+ #:source-url source-url
#:wikiname wikiname
#:title title
#:media-detail media-detail
diff --git a/src/page-search.rkt b/src/page-search.rkt
index 0404233..214a3a2 100644
--- a/src/page-search.rkt
+++ b/src/page-search.rkt
@@ -25,9 +25,10 @@
(define search-json-data
'#hasheq((batchcomplete . #t) (query . #hasheq((search . (#hasheq((ns . 0) (pageid . 219) (size . 1482) (snippet . "") (timestamp . "2022-08-21T08:54:23Z") (title . "Gacha Capsule") (wordcount . 214)) #hasheq((ns . 0) (pageid . 201) (size . 1198) (snippet . "") (timestamp . "2022-07-11T17:52:47Z") (title . "Badges") (wordcount . 181)))))))))
-(define (generate-results-page dest-url wikiname query data #:siteinfo [siteinfo #f])
+(define (generate-results-page req dest-url wikiname query data #:siteinfo [siteinfo #f])
(define search-results (jp "/query/search" data))
(generate-wiki-page
+ #:req req
#:source-url dest-url
#:wikiname wikiname
#:title query
@@ -74,7 +75,7 @@
(define data (easy:response-json dest-res))
- (define body (generate-results-page dest-url wikiname query data #:siteinfo siteinfo))
+ (define body (generate-results-page req dest-url wikiname query data #:siteinfo siteinfo))
(when (config-true? 'debug)
; used for its side effects
; convert to string with error checking, error will be raised if xexp is invalid
diff --git a/src/page-set-user-settings.rkt b/src/page-set-user-settings.rkt
new file mode 100644
index 0000000..3f37692
--- /dev/null
+++ b/src/page-set-user-settings.rkt
@@ -0,0 +1,18 @@
+#lang racket/base
+(require racket/dict
+ net/url
+ web-server/http
+ "application-globals.rkt"
+ "data.rkt"
+ "url-utils.rkt"
+ "xexpr-utils.rkt")
+
+(provide
+ page-set-user-settings)
+
+(define (page-set-user-settings req)
+ (response-handler
+ (define ref (dict-ref (url-query (request-uri req)) 'ref))
+ (define new-settings (read (open-input-string (dict-ref (url-query (request-uri req)) 'new_settings))))
+ (define headers (user-cookies-setter new-settings))
+ (generate-redirect ref #:headers headers)))
diff --git a/src/page-wiki.rkt b/src/page-wiki.rkt
index b2028d6..2cd14dc 100644
--- a/src/page-wiki.rkt
+++ b/src/page-wiki.rkt
@@ -276,6 +276,7 @@
(define (page-wiki req)
(define wikiname (path/param-path (first (url-path (request-uri req)))))
+ (define user-cookies (user-cookies-getter req))
(define origin (format "https://~a.fandom.com" wikiname))
(define path (string-join (map path/param-path (cddr (url-path (request-uri req)))) "/"))
(define source-url (format "https://~a.fandom.com/wiki/~a" wikiname path))
@@ -290,7 +291,9 @@
("formatversion" . "2")
("format" . "json")))))
(log-outgoing dest-url)
- (easy:get dest-url #:timeouts timeouts)]
+ (easy:get dest-url
+ #:timeouts timeouts
+ #:headers `#hasheq((cookie . ,(format "theme=~a" (user-cookies^-theme user-cookies)))))]
[siteinfo (siteinfo-fetch wikiname)])
(cond
@@ -307,6 +310,7 @@
(define body
(generate-wiki-page
(update-tree-wiki page wikiname)
+ #:req req
#:source-url source-url
#:wikiname wikiname
#:title title
@@ -317,9 +321,9 @@
(build-headers
always-headers
(when redirect-msg
- (let* ([dest (get-attribute 'href (bits->attributes ((query-selector (λ (t a c) (eq? t 'a)) redirect-msg))))]
- [value (bytes-append #"0;url=" (string->bytes/utf-8 dest))])
- (header #"Refresh" value)))))
+ (let* ([dest (get-attribute 'href (bits->attributes ((query-selector (λ (t a c) (eq? t 'a)) redirect-msg))))]
+ [value (bytes-append #"0;url=" (string->bytes/utf-8 dest))])
+ (header #"Refresh" value)))))
(when (config-true? 'debug)
; used for its side effects
; convert to string with error checking, error will be raised if xexp is invalid
diff --git a/static/icon-theme-dark.svg b/static/icon-theme-dark.svg
new file mode 100644
index 0000000..bc36ede
--- /dev/null
+++ b/static/icon-theme-dark.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/static/icon-theme-default.svg b/static/icon-theme-default.svg
new file mode 100644
index 0000000..4f5655c
--- /dev/null
+++ b/static/icon-theme-default.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/static/icon-theme-light.svg b/static/icon-theme-light.svg
new file mode 100644
index 0000000..cc19c3a
--- /dev/null
+++ b/static/icon-theme-light.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/static/main.css b/static/main.css
index 0976e72..c6b2791 100644
--- a/static/main.css
+++ b/static/main.css
@@ -51,7 +51,7 @@ p {
.custom-top {
display: flex;
flex-wrap: wrap;
- align-items: baseline;
+ align-items: center;
justify-content: space-between;
}
.page-title {
@@ -227,6 +227,7 @@ figcaption, .lightbox-caption, .thumbcaption {
display: grid;
grid-template-columns: auto 1fr;
grid-gap: 0px 5px;
+ align-items: baseline;
}
.bw-ss__container {
grid-column: 2;
@@ -282,6 +283,47 @@ figcaption, .lightbox-caption, .thumbcaption {
text-align: left;
}
+/* (breezewiki) theme selector */
+.bw-theme__select {
+ display: grid;
+ grid-template-columns: auto auto;
+ grid-gap: 0px 5px;
+ justify-content: right;
+ align-items: baseline;
+ margin-top: 4px;
+}
+.bw-theme__items {
+ display: flex;
+}
+.bw-theme__item {
+ display: flex;
+ align-items: baseline;
+ padding: 2px;
+ border: 1px solid var(--theme-border-color);
+ border-right-width: 0px;
+ background-color: var(--custom-table-background);
+ color: var(--theme-page-text-color);
+ transition: none;
+}
+.bw-theme__item:hover, .bw-theme__item:focus {
+ /* background-color: var(--theme-page-background-color); */
+ color: var(--theme-accent-color);
+}
+.bw-theme__item:first-child {
+ border-radius: 4px 0px 0px 4px;
+}
+.bw-theme__item:last-child {
+ border-radius: 0px 4px 4px 0px;
+ border-right-width: 1px;
+}
+.bw-theme__item--selected, .bw-theme__item--selected:hover, .bw-theme__item--selected:focus {
+ background-color: var(--theme-accent-color);
+ color: var(--theme-accent-label-color);
+}
+.bw-theme__icon-container svg {
+ vertical-align: middle;
+}
+
/* nintendo independent wiki alliance notice */
.niwa__notice {
background: #fdedd8;