From 43af72aa4d46bc542e6e2efeef2fa1d6e461eadf Mon Sep 17 00:00:00 2001 From: rhiannon morris Date: Mon, 5 Aug 2024 23:36:30 +0200 Subject: [PATCH 1/3] make the warning dialog a --- make-pages/NsfwWarning.hs | 34 +++---- script/nsfw-warning.ts | 16 ++-- style/shiny/nsfw-warning.css | 169 +++++++++++++++++------------------ 3 files changed, 97 insertions(+), 122 deletions(-) diff --git a/make-pages/NsfwWarning.hs b/make-pages/NsfwWarning.hs index 1f4066b..db02958 100644 --- a/make-pages/NsfwWarning.hs +++ b/make-pages/NsfwWarning.hs @@ -17,27 +17,15 @@ script (Just _) = [b||] dialog :: Maybe What -> Builder dialog Nothing = "" dialog (Just what) = [b|@0 -
-
-

cw: lewd art

- - - -
-

- $what contains pornographic content that is - not suitable for minors. -

- by continuing, you are confirming that you are at least - eighteen years old. -

- -
- - - - -
-
-
+ +

cw: lewd art

+ +
you must be an adult to view $what. no minors!
+
+ + + + +
+
|] diff --git a/script/nsfw-warning.ts b/script/nsfw-warning.ts index 25a783a..5b80e72 100644 --- a/script/nsfw-warning.ts +++ b/script/nsfw-warning.ts @@ -1,21 +1,17 @@ const nsfwOk = 'nsfw-ok'; - -function dismiss() { - const dialog = document.getElementById('nsfw-dialog')!; - dialog.parentElement!.removeChild(dialog); -} +const dialog = document.getElementById('nsfw-dialog')! as HTMLDialogElement; function yes() { localStorage.setItem(nsfwOk, '1'); - dismiss(); + dialog.close(); } function setup() { - if (localStorage.getItem(nsfwOk)) { - dismiss(); - } else { - document.getElementById('nsfw-yes')!.onclick = yes; + if (!localStorage.getItem(nsfwOk)) { + (dialog.querySelector('#nsfw-yes') as HTMLElement).onclick = yes; // nsfw-no is a normal link + + dialog.showModal(); } } diff --git a/style/shiny/nsfw-warning.css b/style/shiny/nsfw-warning.css index fc72ba2..139bbe9 100644 --- a/style/shiny/nsfw-warning.css +++ b/style/shiny/nsfw-warning.css @@ -1,110 +1,101 @@ -.dialog { - position: fixed; - top: 0; left: 0; - width: 100vw; height: 100vh; - margin: 0; - z-index: 1000; +dialog { + &[open] { + &::backdrop { + background: hsl(340deg, 35%, 15%, 90%); + mix-blend-mode: multiply; + } + ~ .page { filter: blur(15px); } + + place-self: center; + + display: grid; + grid-template: + "icon header" + "icon text" + "icon buttons" + / 1fr 3fr; + gap: 0.5em 1.5em; + place-items: center; + place-content: center; + + min-height: 20vh; + max-width: 30em; + padding: 1.5em 3em 2em; + + background: var(--background); + border: var(--border); + border-radius: var(--border-radius); + box-shadow: 0 0 50px #fff6; + } - background: hsl(340deg, 35%, 15%, 90%); color: var(--text-col); - display: grid; -} -.dialog ~ .page { - filter: blur(15px); -} + h1 { + margin: 0; + grid-area: header; + } -.dialog-inner { - place-self: center; + img { + grid-area: icon; + } - display: grid; - grid-template: - "header header" - "icon text" - "buttons buttons" - / 1fr 3fr; - grid-gap: 0.5em; - align-items: center; + div { + grid-area: text; + text-align: center; + font-size: 125%; + } - min-height: 20vh; - max-width: 30em; - padding: 1.5em 3em 2em; + form { + grid-area: buttons; - background: var(--background); - border: var(--border); - border-radius: var(--border-radius); -} + display: flex; + justify-content: center; -.dialog h1 { - margin: 0; - grid-area: header; - justify-self: center; -} + margin-top: 1em; -.dialog-icon { - height: 3em; - width: 3em; - grid-area: icon; - justify-self: center; -} + > * { + margin: 0 1em; + } + } -.dialog-message { - grid-area: text; - justify-self: start; - font-size: 125%; -} + button { + border: none; + border-radius: 0.75em; + padding: 0.5em 1em; -.dialog-buttons { - grid-area: buttons; - justify-self: center; + font-family: Muller; + font-weight: 600; + font-size: inherit; + color: black; - display: flex; - justify-content: center; + position: relative; - margin-top: 1em; -} + cursor: pointer; -.dialog-buttons > * { - margin: 0 1em; -} + a { + inset: 0; + width: max-content; + text-decoration: none; + } + } -.dialog button { - border: none; - border-radius: 0.75em; - padding: 0.5em 1em; + .yes { + background: hsl(100deg, 70%, 80%); + } - font-family: Muller; - font-weight: 600; - font-size: inherit; - color: black; + .no { + background: hsl(5deg, 70%, 80%); + } - position: relative; + p { + text-align: left; + -ms-hyphens: none; + hyphens: none; + } - cursor: pointer; -} - -.dialog button a { - inset: 0; - width: max-content; - text-decoration: none; -} - -.dialog .yes { - background: hsl(100deg, 70%, 80%); -} - -.dialog .no { - background: hsl(5deg, 70%, 80%); -} - -.dialog p { - text-align: left; - -ms-hyphens: none; - hyphens: none; -} - -.dialog strong { - font-weight: 800; + strong { + font-weight: 800; + } } @media (pointer: coarse) { From 969cdc938d01a7f48999524e518785fd24009e41 Mon Sep 17 00:00:00 2001 From: rhiannon morris Date: Sun, 18 Aug 2024 02:43:41 +0200 Subject: [PATCH 2/3] refactor fragment aliases & add field lookup checking why is that not the default --- Makefile | 2 +- script/gallery.ts | 27 +++++++++++++++++---------- tsconfig.json | 1 + 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index d2bd1c3..b27b287 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ $(BUILDDIR)/%: $(TMPDIR)/% $(TMPDIR)/%.js: %.ts echo "[tsc] "$@ - tsc --strict --noEmitOnError \ + tsc --strict --noUncheckedIndexedAccess --noEmitOnError \ --lib dom,es2021 --target es2015 \ --outDir $(dir $@) $^ diff --git a/script/gallery.ts b/script/gallery.ts index 1213ef8..6c562cd 100644 --- a/script/gallery.ts +++ b/script/gallery.ts @@ -106,23 +106,30 @@ function makeFragment() { } } +type Shortcuts = { [short: string]: Set }; +const shortcuts: Shortcuts = { + 'summary': new Set('require_artsummary'), + 'colourexamples': new Set('require_colourexample'), + 'flatexamples': new Set('require_flatexample'), + 'sketchexamples': new Set('require_sketchexample'), + 'iconexamples': new Set('require_iconexample'), + 'curated': new Set('require_curated') +}; + function useFragment() { const frag = decodeURIComponent(location.hash).replace(/^#/, ''); const details = document.getElementById('filters-details') as HTMLDetailsElement; + const fromShortcut = shortcuts[frag]; if (!frag) { clearForm(); } else if (frag == 'all') { allBoxes.forEach(b => b.checked = false); details.open = false; + } else if (fromShortcut) { + allBoxes.forEach(b => b.checked = fromShortcut.has(b.id)); } else { - const pieces = - frag == 'summary' ? ['require_artsummary'] : - frag == 'colourexamples' ? ['require_colourexample'] : - frag == 'flatexamples' ? ['require_flatexample'] : - frag == 'sketchexamples' ? ['require_sketchexample'] : - frag == 'iconexamples' ? ['require_iconexample'] : - frag == 'curated' ? ['require_curated'] : frag.split(';'); + const pieces = frag.split(';'); const set = new Set(pieces); const re = /^(require|exclude)_|hide_filters/; if (pieces.every(x => re.test(x))) { @@ -154,7 +161,7 @@ function sortFilters(cmp: (a: Node, b: Node) => number) { function sortFiltersAlpha(e: Event) { function getName(node: Node): string { if (node instanceof Element) { - return node.getElementsByTagName('input')[0].value; + return node.getElementsByTagName('input')[0]?.value ?? ''; } else { return ''; } @@ -164,9 +171,9 @@ function sortFiltersAlpha(e: Event) { } function sortFiltersUses(e: Event) { - function getUses(node: Node) { + function getUses(node: Node): number { if (node instanceof Element) { - const countStr = node.getElementsByTagName('label')[0].dataset.count; + const countStr = node.getElementsByTagName('label')[0]?.dataset.count; return countStr ? +countStr : 0; } else { return 0; diff --git a/tsconfig.json b/tsconfig.json index 4c1a853..309f42d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "strict": true, + "noUncheckedIndexedAccess": true, "noEmitOnError": true, "lib": ["ES2021", "dom"], "target": "ES2015" From fa0b826c26c94649f8bd6a9d81c87414cdd7af1a Mon Sep 17 00:00:00 2001 From: rhiannon morris Date: Sun, 18 Aug 2024 06:22:55 +0200 Subject: [PATCH 3/3] =?UTF-8?q?add=20more=20details=20(and=20the=20image?= =?UTF-8?q?=E2=80=BC)=20to=20rss?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- make-pages/GalleryPage.hs | 5 +--- make-pages/Info.hs | 11 +++++-- make-pages/Main.hs | 13 ++++---- make-pages/RSS.hs | 62 +++++++++++++++++++++++++-------------- make-pages/SinglePage.hs | 23 ++++++++------- 5 files changed, 69 insertions(+), 45 deletions(-) diff --git a/make-pages/GalleryPage.hs b/make-pages/GalleryPage.hs index c69b0da..54b3243 100644 --- a/make-pages/GalleryPage.hs +++ b/make-pages/GalleryPage.hs @@ -34,7 +34,7 @@ make' root (GalleryInfo {title, desc, prefix, filters, hidden}) infos = [b|@0 - + @@ -108,9 +108,6 @@ make' root (GalleryInfo {title, desc, prefix, filters, hidden}) infos = [b|@0 nsfw = filters.nsfw /= NoNsfw url = [b|$root/$prefix|] - imagepath0 - | (_, (p₀, i₀) : _) : _ <- infosByYear = getThumb (takeDirectory p₀) i₀ - | otherwise = "/style/card.png" nsfw' = NsfwWarning.Gallery <$ guard nsfw nsfwScript = NsfwWarning.script nsfw' diff --git a/make-pages/Info.hs b/make-pages/Info.hs index dd407ce..e09e294 100644 --- a/make-pages/Info.hs +++ b/make-pages/Info.hs @@ -13,6 +13,7 @@ module Info CompareKey (..), compareKeyFor, compareFor, sortFor, Artist (..), Images' (..), Images, Image (..), Desc (..), DescField (..), + PreviewImage (..), previewImage, Link (..), Update (..), Bg (..), @@ -117,6 +118,13 @@ data Images' a = type Images = Images' Image +data PreviewImage = PFull Image | PThumb FilePath + +previewImage :: Info -> Maybe PreviewImage +previewImage info + | Just img <- find (.sfw) $ allImages info.images = Just $ PFull img + | otherwise = PThumb <$> info.thumb' + data Link = Link { @@ -261,8 +269,7 @@ newtype NoThumb = NoThumb FilePath instance Show NoThumb where show (NoThumb dir) = "no thumbnail for " ++ dir getThumb :: FilePath -> Info -> FilePath -getThumb dir = - maybe (throw $ NoThumb dir) (\t -> dir thumbFile t) . thumb +getThumb dir = maybe (throw $ NoThumb dir) (\t -> dir thumbFile t) . thumb thumbFile :: FilePath -> FilePath thumbFile = addSuffix "_small" diff --git a/make-pages/Main.hs b/make-pages/Main.hs index 0c4ca25..959148f 100644 --- a/make-pages/Main.hs +++ b/make-pages/Main.hs @@ -8,6 +8,7 @@ import Data.List (intersperse) import qualified Data.List as List import Data.Text.Lazy (Text) import Data.Text.Lazy.Builder (toLazyText) +import qualified Data.Text as Strict import qualified Data.Text.Lazy.IO as Text import qualified Data.YAML as YAML import System.FilePath (makeRelative, takeDirectory, takeFileName) @@ -56,7 +57,7 @@ main2 (SinglePage {root, file, prefix, index, dataDir, nsfw, output}) = do writeOutput output page main2 (GalleryPage {root, files, prefix, index, output, dataDir}) = do - ginfo <- galleryFromIndex index prefix + (_, ginfo) <- galleryFromIndex index prefix printV $ "gallery_info" := ginfo infos <- mapM (infoYAML dataDir) files printV $ "infos" := infos @@ -70,12 +71,12 @@ main2 (IndexPage {root, file, output}) = do writeOutput output page main2 (RSS {files, root, index, prefix, output, dataDir}) = do - ginfo <- galleryFromIndex index prefix + (name, ginfo) <- galleryFromIndex index prefix printV $ "gallery_info" := ginfo infos <- mapM (infoYAML dataDir) files printV $ "infos" := infos let output' = takeFileName <$> output - let rss = RSS.make root ginfo output' infos + let rss = RSS.make root name ginfo output' infos writeOutput output rss main2 (DependSingle {index, file, nsfw, output, prefix, buildDir, dataDir}) = do @@ -117,10 +118,10 @@ findInfos dataDir infoName = readYAML :: YAML.FromYAML a => FilePath -> IO a readYAML file = ByteString.readFile file >>= decode1Must file -galleryFromIndex :: FilePath -> FilePath -> IO GalleryInfo +galleryFromIndex :: FilePath -> FilePath -> IO (Strict.Text, GalleryInfo) galleryFromIndex file prefix = do - IndexInfo {galleries} <- readYAML file - maybe (fail $ "no gallery with prefix " ++ prefix) pure $ + IndexInfo {title, galleries} <- readYAML file + maybe (fail $ "no gallery with prefix " ++ prefix) (pure . (title,)) $ List.find (\g -> g.prefix == prefix) galleries decode1Must :: YAML.FromYAML a => FilePath -> ByteString -> IO a diff --git a/make-pages/RSS.hs b/make-pages/RSS.hs index 8a75604..ba12587 100644 --- a/make-pages/RSS.hs +++ b/make-pages/RSS.hs @@ -4,8 +4,8 @@ import Date import Info import BuilderQQ -import Data.List (sortBy) -import Data.Maybe (isJust) +import Data.List (sortBy, intersperse) +import Data.Maybe (catMaybes) import Data.Function (on) import qualified Data.Text as Strict import qualified Data.Text.Lazy as Lazy @@ -13,20 +13,21 @@ import System.FilePath (takeDirectory) make :: Strict.Text -- ^ website root e.g. @https://gallery.niss.website@ + -> Strict.Text -- ^ website name e.g. @nissart@ -> GalleryInfo -> Maybe FilePath -- ^ output filename for self link -> [(FilePath, Info)] -> Lazy.Text -make root ginfo output infos = - toLazyText $ make' root ginfo output infos +make root name ginfo output infos = + toLazyText $ make' root name ginfo output infos -make' :: Strict.Text -> GalleryInfo +make' :: Strict.Text -> Strict.Text -> GalleryInfo -> Maybe FilePath -> [(FilePath, Info)] -> Builder -make' root ginfo@(GalleryInfo {title, desc, prefix}) output infos = [b|@0 +make' root name ginfo@(GalleryInfo {title, desc, prefix}) output infos = [b|@0 - + - $title + $name—$title $link $desc $selflink @@ -43,37 +44,54 @@ make' root ginfo@(GalleryInfo {title, desc, prefix}) output infos = [b|@0 filter (not . (.unlisted) . snd) infos selflink = case output of Nothing -> "" - Just o -> [b||] + Just o -> [b||] makeItem :: Strict.Text -> FilePath -> Bool -> FilePath -> Info -> Builder makeItem root prefix nsfw path i@(Info {title, artist}) = [b|@4 - $title$up + $title$suf $link $link - $descArtist' + $body $date |] where - up = if hasUpdatesFor nsfw i then [b| (updated)|] else "" + suf = let parts = catMaybes [o18, cnt, up] in + if null parts then "" + else " (" <> mconcat (intersperse ", " parts) <> ")" + up = if hasUpdatesFor nsfw i then Just "updated" else Nothing + o18 = if nsfw && anyNsfw i then Just "🔞" else Nothing + cnt = let len = maybe 0 length $ allImages <$> imagesFor nsfw i in + if len == 1 then Nothing else Just [b|$len images|] + dir = takeDirectory path link = [b|$root/$prefix/$dir|] + + date = formatRSS $ latestDateFor nsfw i artist' = ifJust artist \case Artist {name, url = Nothing} -> [b|

by $name|] - Artist {name, url = Just url} -> [b|

by $name|] + Artist {name, url = Just url} -> [b|

by $name|] desc = descFor nsfw i desc' = makeDesc desc - descArtist' = if desc.exists || isJust artist then [b|@6 - - - + + body = [b|@6 + |] - else "" - date = formatRSS $ latestDateFor nsfw i + + image = case previewImage i of + Just (PFull img) -> go $ pageFile img + Just (PThumb th) -> go $ thumbFile th + Nothing -> "" + where go p = [b|@0 +

+ +
+ |] makeDesc :: Desc -> Builder makeDesc NoDesc = "" diff --git a/make-pages/SinglePage.hs b/make-pages/SinglePage.hs index b2644a3..2852716 100644 --- a/make-pages/SinglePage.hs +++ b/make-pages/SinglePage.hs @@ -9,7 +9,7 @@ import qualified NsfwWarning import Control.Exception import Control.Monad import Data.List (sort, intersperse) -import Data.Maybe (fromMaybe, isNothing, isJust) +import Data.Maybe (fromMaybe, isJust) import qualified Data.Text as Strict import qualified Data.Text.Lazy as Lazy import System.FilePath (joinPath, splitPath) @@ -94,7 +94,6 @@ make' root siteName prefix nsfw _dataDir dir let desc = case artist of Just (Artist {name}) -> [b|by $name|] Nothing -> "by niss" - let thumbnail = getThumb "" info let updateDate = ifJust (last' updates) \(d, _) -> let updated = formatLong d in @@ -104,15 +103,17 @@ make' root siteName prefix nsfw _dataDir dir let nsfwScript = NsfwWarning.script nsfw' let nsfwDialog = NsfwWarning.dialog nsfw' - let imageMeta = - if image0.sfw && isNothing image0.warning then [b|@0 - - - - |] else [b|@0 - - - |] + let imageMeta = case previewImage info of + Just (PFull (Image {path})) -> [b|@0 + + + + |] + Just (PThumb path) -> [b|@0 + + + |] + Nothing -> throw $ NoThumb dir pure [b|@0