gallery/make-pages/SinglePage.hs

338 lines
9.3 KiB
Haskell
Raw Normal View History

2020-07-09 06:20:09 +02:00
module SinglePage (make) where
2020-07-08 05:28:09 +02:00
2020-09-25 23:08:44 +02:00
import Date
2020-08-11 20:29:54 +02:00
import Info
2020-07-15 21:31:46 +02:00
import BuilderQQ
2020-07-16 16:07:28 +02:00
import Records ()
2020-10-06 22:07:39 +02:00
import qualified NsfwWarning
2020-07-13 04:01:31 +02:00
import Control.Exception
2022-01-03 20:45:55 +01:00
import Control.Monad
import Data.List (sort, intersperse)
2022-12-26 22:04:57 +01:00
import Data.Maybe (fromMaybe, isNothing, isJust)
2020-07-08 05:28:09 +02:00
import qualified Data.Text as Strict
import qualified Data.Text.Lazy as Lazy
2022-05-16 10:25:16 +02:00
import System.FilePath (joinPath, splitPath)
2021-08-23 16:35:55 +02:00
import qualified Data.HashSet as Set
import Data.Traversable
2020-07-13 05:02:16 +02:00
-- | e.g. only nsfw images are present for a non-nsfw page
data NoEligibleImages = NoEligibleImages {title :: !Strict.Text}
deriving stock Eq deriving anyclass Exception
instance Show NoEligibleImages where
show (NoEligibleImages {title}) =
Strict.unpack title <> ": no images selected\n" <>
" (probably a nsfw-only work without --nsfw set)"
2020-07-08 05:28:09 +02:00
2020-08-11 20:29:54 +02:00
make :: Text -- ^ website root
-> Text -- ^ website name
2020-08-11 20:29:54 +02:00
-> FilePath -- ^ gallery prefix
-> Bool -- ^ nsfw?
2020-08-09 01:22:00 +02:00
-> FilePath -- ^ data dir
-> FilePath -- ^ subdir of datadir containing this @info.yaml@
-> Info -> IO Lazy.Text
make root siteName prefix nsfw dataDir dir info =
toLazyText <$> make' root siteName prefix nsfw dataDir dir info
2020-07-08 05:28:09 +02:00
make' :: Text -> Text -> FilePath -> Bool -> FilePath -> FilePath -> Info
-> IO Builder
make' root siteName prefix nsfw _dataDir dir
info@(Info {date, title, artist, bg}) = do
2022-05-16 10:25:16 +02:00
let images = imagesFor nsfw info
2020-08-09 01:22:00 +02:00
let undir = joinPath (replicate (length (splitPath dir)) "..")
let artistTag = ifJust artist makeArtist
2020-09-25 23:08:44 +02:00
let formattedDate = formatLong date
2020-08-09 01:22:00 +02:00
2021-08-23 16:35:55 +02:00
let buttonBar = makeButtonBar title $ addIds images
2022-05-16 10:25:16 +02:00
let image0@(Image {path = path0, download = download0'}) : otherImages =
#all images
let download0 = fromMaybe (bigFile path0) download0'
2020-08-09 01:22:00 +02:00
let path0' = pageFile path0
let descSection = makeDesc $ descFor nsfw info
let tagsList = makeTags undir $ tagsFor nsfw info
let linksList = extLinks $ linksFor nsfw info
2020-11-16 23:30:56 +01:00
let updates = sort $ updatesFor nsfw info
let updatesList = makeUpdates updates
2020-08-09 01:22:00 +02:00
2022-05-16 10:25:16 +02:00
let makePrefetch (Image {path}) = [b|<link rel=prefetch href=$path'>|]
where path' = bigFile path
let prefetches = map makePrefetch otherImages
2020-08-09 01:22:00 +02:00
2020-10-06 22:07:39 +02:00
let makeWarning w = [b|@0
2020-08-09 01:22:00 +02:00
<figcaption id=cw aria-role=button tabindex=0>
2020-09-14 02:35:56 +02:00
<span id=cw-text>$w</span>
2020-08-09 01:22:00 +02:00
</figcaption>
|]
2022-12-26 20:09:25 +01:00
let defWarning = [b|
i forgot to add a cw, sorry! <br>
if you can let me know i'd appreciate it
|]
2021-06-04 04:07:05 +02:00
let warning'
| Just w <- #warning image0 = makeWarning w
| #nsfw image0 = makeWarning defWarning
| otherwise = mempty
2020-09-14 02:34:21 +02:00
let warningT = makeWarning [b|.|]
let bgStyle = ifJust bg \col -> [b|@0
<style> #mainfig { background: $col; } </style>
|]
2020-08-09 01:22:00 +02:00
let url = [b|$root/$prefix/$dir|]
2020-08-11 20:29:54 +02:00
let desc = case artist of
Just (Artist {name}) -> [b|by $name|]
2020-08-11 20:29:54 +02:00
Nothing -> "by niss"
let thumb = getThumb "" info
let updateDate = ifJust (last' updates) \(d, _) ->
2020-11-16 23:30:56 +01:00
let updated = formatLong d in
[b|<br> <span class=updated>updated $updated</span>|]
2020-09-19 07:51:52 +02:00
2022-01-03 20:45:55 +01:00
let nsfw' = NsfwWarning.Single <$ guard nsfw
let nsfwScript = NsfwWarning.script nsfw'
let nsfwDialog = NsfwWarning.dialog nsfw'
2020-10-06 22:07:39 +02:00
let imageMeta =
if #sfw image0 && isNothing (#warning image0) then [b|@0
<meta property=og:image content="$url/$path0'">
<meta name=twitter:card content=summary_large_image>
<meta name=twitter:image content="$url/$path0'">
|] else [b|@0
<meta property=og:image content="$url/$thumb">
<meta name=twitter:card content=summary>
|]
2020-08-09 01:22:00 +02:00
pure [b|@0
2020-07-13 04:01:31 +02:00
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
2020-08-04 18:59:09 +02:00
<meta name=viewport content="width=1200,viewport-fit=cover">
2020-08-04 23:12:58 +02:00
<link rel=stylesheet href=/style/shiny/single.css>
2020-08-05 00:52:56 +02:00
<link rel=icon href=/style/niss.svg>
2020-07-17 12:29:13 +02:00
<meta property=og:type content=article>
<meta property=og:title content="$title">
<meta property=og:site_name content="$siteName">
2020-08-11 20:29:54 +02:00
<meta property=og:description content="$desc">
<meta property=og:url content="$url">
2020-09-19 08:13:13 +02:00
<meta name=twitter:site content=@2_gecs>
$imageMeta
2020-08-11 20:29:54 +02:00
2022-11-12 12:14:42 +01:00
<meta name=robots content='noai,noimageai'>
2020-08-04 19:14:12 +02:00
<script src=/script/single.js></script>
2020-10-06 22:07:39 +02:00
$nsfwScript
$bgStyle
2020-08-03 19:32:40 +02:00
2020-07-25 13:59:04 +02:00
$0.prefetches
<title>$title</title>
2020-07-13 04:01:31 +02:00
2020-10-06 22:07:39 +02:00
$nsfwDialog
2020-07-13 04:01:31 +02:00
2020-10-06 22:07:39 +02:00
<div class=page>
<header>
<h1>$title</h1>
$artistTag
<h2 id=date class="right corner">
$formattedDate $updateDate
</h2>
</header>
2020-07-17 12:29:13 +02:00
2020-10-06 22:07:39 +02:00
$2.buttonBar
2020-07-13 04:01:31 +02:00
2020-10-06 22:07:39 +02:00
<main>
2022-05-16 10:25:16 +02:00
<figure id=mainfig>
2020-10-06 22:07:39 +02:00
$warning'
<a id=mainlink href="$download0" title="download full version">
<img id=mainimg src="$path0'" alt="">
</a>
</figure>
2020-07-13 04:01:31 +02:00
2020-10-06 22:07:39 +02:00
<div id=info>
$6.descSection
2020-07-13 04:01:31 +02:00
2022-02-27 01:03:28 +01:00
$6.updatesList
2020-09-19 07:51:52 +02:00
2020-10-06 22:07:39 +02:00
$6.linksList
2022-02-27 01:03:28 +01:00
$6.tagsList
2020-10-06 22:07:39 +02:00
</div>
</main>
2020-07-13 04:01:31 +02:00
2020-10-06 22:07:39 +02:00
<footer>
<a href=$undir>back to gallery</a>
</footer>
</div>
2020-09-14 02:34:21 +02:00
<template id=cw-template>
$warningT
</template>
2020-07-13 04:01:31 +02:00
|]
2020-07-17 12:29:13 +02:00
2020-11-16 23:30:56 +01:00
last' :: [a] -> Maybe a
last' xs = if null xs then Nothing else Just $ last xs
2020-07-14 06:51:46 +02:00
makeArtist :: Artist -> Builder
makeArtist (Artist {name, url}) =
2020-07-19 18:03:24 +02:00
[b|<h2 id=artist class="left corner">by $artistLink</h2>|]
2020-07-14 06:51:46 +02:00
where
artistLink = case url of
Just u -> [b|<a href="$u">$name</a>|]
Nothing -> [b|$name|]
2020-07-14 06:51:46 +02:00
makeDesc :: Desc -> Builder
makeDesc NoDesc = ""
makeDesc (TextDesc desc) = [b|@0
<section id=desc class=info-section>
2020-08-03 19:36:48 +02:00
<h2>about</h2>
<div>
2020-10-06 22:07:39 +02:00
$4.desc
2020-08-03 19:36:48 +02:00
</div>
</section>
2020-07-13 04:01:31 +02:00
|]
makeDesc (LongDesc fs) = [b|@0
<section id=desc class=info-section>
$2.fields
</section>
|]
where
fields = map makeField fs
makeField (DescField {name, text}) = [b|@0
<h2>$name</h2>
<div>
$4.text
</div>
|]
2020-07-08 05:28:09 +02:00
2022-05-16 10:25:16 +02:00
addIds :: Traversable t => t Image -> t (Image, Text)
2021-08-23 16:35:55 +02:00
addIds = snd . mapAccumL makeId Set.empty where
2022-05-16 10:25:16 +02:00
makeId used img = (Set.insert newId used, (img, newId)) where
2021-08-23 16:35:55 +02:00
newId = head $ filter (\i -> not $ i `Set.member` used) ids
ids = [toStrictText [b|$label$i|] | i <- "" : map show [0::Int ..]]
label = escId $ #label img
2022-05-16 10:25:16 +02:00
makeButtonBar :: Strict.Text -> Images' (Image, Text) -> Builder
2020-07-25 13:58:53 +02:00
makeButtonBar title images =
2021-03-13 05:30:28 +01:00
case images of
2021-08-23 16:30:11 +02:00
Uncat [] -> throw $ NoEligibleImages title
Uncat [_] -> ""
Cat [(_,[_])] -> ""
Uncat imgs -> makeNav "uncat" $ makeAlts imgs
Cat cats
| all ((<= 1) . length . snd) cats ->
makeButtonBar title $ Uncat $ flatten cats
2022-11-12 12:13:02 +01:00
| [(_, imgs)] <- cats ->
makeButtonBar title (Uncat imgs)
| otherwise ->
makeNav "cat" $ map (uncurry makeCat) cats
2021-08-23 16:30:11 +02:00
where
2022-12-26 22:04:57 +01:00
makeNav (cls :: Text) inner = [b|@0
<nav id=alts class=$cls aria-label="alternate versions">
$2.inner
$2.skipAll
</nav> |]
makeCat lbl imgs = [b|@0
<section>
<h3 class=alt-cat>$lbl</h3>
$0.alts
</section> |]
where alts = makeAlts imgs
makeAlts imgs = [b|@0
<ul class="buttonbar bb-choice">
$2.elems
</ul> |]
where elems = map (\(img,i) -> altButton img i) imgs
skipAll =
if any (isJust . #warning . fst) images then
[b|@0
<div class=buttonbar id=skipAllDiv>
<input type=checkbox name=skipAll id=skipAll>
<label for=skipAll>skip warnings</label>
</div>
|]
else
""
2022-05-16 10:25:16 +02:00
flatten :: [(Text, [(Image, a)])] -> [(Image, Text)]
flatten cats =
2022-05-16 10:25:16 +02:00
addIds [(img {label = cat}) | (cat, is) <- cats, (img, _) <- is]
2022-05-16 10:25:16 +02:00
altButton :: Image -> Text -> Builder
altButton img i = [b|@0
2020-07-13 04:01:31 +02:00
<li$nsfwClass>
2021-08-23 16:35:55 +02:00
<input type=radio name=variant id="$i" value="$path'"
2022-05-16 10:25:16 +02:00
data-link="$link"$warning'>
2021-08-23 16:35:55 +02:00
<label for="$i"$nsfwLabelClass>$label</label>
2020-07-13 04:01:31 +02:00
|]
2020-07-08 05:28:09 +02:00
where
2021-02-09 12:50:22 +01:00
Image {label, path, nsfw, warning, download} = img
nsfwClass = if nsfw then [b| class=nsfw|] else ""
nsfwLabelClass = if nsfw then [b| class=nsfw-label|] else ""
path' = pageFile path
2022-05-16 10:25:16 +02:00
link = fromMaybe (bigFile path) download
2020-09-14 02:33:27 +02:00
warning' = ifJust warning \(escAttr -> w) -> [b| data-warning="$w"|]
2020-07-08 05:28:09 +02:00
2020-08-04 19:05:20 +02:00
makeTags :: FilePath -> [Strict.Text] -> Builder
makeTags undir tags =
2020-10-06 22:07:39 +02:00
if null tags then "" else [b|@0
2020-08-04 18:26:36 +02:00
<nav id=tags class=info-section>
2020-07-13 04:01:31 +02:00
<h2>tags</h2>
<ul class="buttonbar bb-links">
2020-10-06 22:07:39 +02:00
$4.tagList
2020-07-13 04:01:31 +02:00
</ul>
2020-08-04 18:26:36 +02:00
</nav>
2020-07-13 04:01:31 +02:00
|]
2020-07-12 05:42:31 +02:00
where
2020-07-13 04:01:31 +02:00
tagList = map makeTag tags
makeTag tag = [b|<li><a href="$undir#require_$tag'">$tag</a>|]
2020-08-04 19:05:20 +02:00
where tag' = escId tag
2020-07-12 05:42:31 +02:00
2020-08-03 19:36:48 +02:00
extLinks :: [Link] -> Builder
extLinks links =
2020-10-06 22:07:39 +02:00
if null links then "" else [b|@0
2020-08-04 18:26:36 +02:00
<nav id=links class=info-section>
2020-07-13 04:01:31 +02:00
<h2>links</h2>
2020-08-04 18:26:36 +02:00
<ul class="buttonbar bb-links">
2020-10-06 22:07:39 +02:00
$4.linkList
2020-07-13 04:01:31 +02:00
</ul>
2020-08-04 18:26:36 +02:00
</nav>
2020-07-13 04:01:31 +02:00
|]
2020-08-03 19:36:48 +02:00
where linkList = map extLink links
2020-07-08 05:28:09 +02:00
extLink :: Link -> Builder
2020-08-04 18:26:36 +02:00
extLink (Link {title, url}) = [b|@8
2020-07-13 04:01:31 +02:00
<li>
<a href="$url">
$title
2020-07-13 04:01:31 +02:00
</a>
|]
2020-08-01 02:27:24 +02:00
makeUpdates :: [(Date, [Update])] -> Builder
2020-09-19 07:51:52 +02:00
makeUpdates ups =
2022-11-12 12:13:02 +01:00
if all (null . snd) ups then "" else [b|@4
2020-09-19 07:51:52 +02:00
<section id=updates class=info-section>
<h2>updates</h2>
<dl>
$8.updateList
</dl>
</section>
|]
where updateList = map (uncurry makeUpdate) ups
2020-09-19 07:51:52 +02:00
makeUpdate :: Date -> [Update] -> Builder
2022-11-12 12:13:02 +01:00
makeUpdate _ [] = ""
makeUpdate date ups = [b|@8
2020-09-19 07:51:52 +02:00
<dt>$date'
2020-11-16 23:30:56 +01:00
<dd>$desc
|] where
date' = formatSlash date
desc = mconcat $ map fromText $ intersperse "; " $ map #desc ups