neuron-1.0.0.0: src/lib/Neuron/Web/Zettel/View.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Neuron.Web.Zettel.View
( renderZettel,
renderZettelContentCard,
)
where
import Data.Some
import Data.TagTree
import qualified Neuron.Web.Query.View as Q
import Neuron.Web.Route
import Neuron.Web.Widget
import qualified Neuron.Web.Widget.AutoScroll as AS
import qualified Neuron.Web.Widget.InvertedTree as IT
import Neuron.Zettelkasten.Connection
import Neuron.Zettelkasten.Graph (ZettelGraph)
import qualified Neuron.Zettelkasten.Graph as G
import Neuron.Zettelkasten.Query.Error (QueryError, showQueryError)
import qualified Neuron.Zettelkasten.Query.Eval as Q
import Neuron.Zettelkasten.Zettel
import Reflex.Dom.Core hiding ((&))
import Reflex.Dom.Pandoc
import Relude hiding ((&))
import Text.Pandoc.Definition (Pandoc)
renderZettel ::
PandocBuilder t m =>
(ZettelGraph, ZettelC) ->
NeuronWebT t m ()
renderZettel (graph, zc@(sansContent -> z)) = do
let upTree = G.backlinkForest Folgezettel z graph
unless (null upTree) $ do
IT.renderInvertedHeadlessTree "zettel-uptree" "deemphasized" upTree $ \z2 ->
Q.renderZettelLink Nothing (G.getConnection z z2 graph) def z2
-- Main content
elAttr "div" ("class" =: "ui text container" <> "id" =: "zettel-container" <> "style" =: "position: relative") $ do
whenStaticallyGenerated $ do
-- We use -24px (instead of -14px) here so as to not scroll all the way to
-- title, and as to leave some of the tree visible as "hint" to the user.
lift $ AS.marker "zettel-container-anchor" (-24)
divClass "zettel-view" $ do
renderZettelContentCard (graph, zc)
renderZettelBottomPane graph z
-- Because the tree above can be pretty large, we scroll past it
-- automatically when the page loads.
whenStaticallyGenerated $ do
unless (null upTree) $ do
AS.script "zettel-container-anchor"
renderZettelContentCard ::
PandocBuilder t m =>
(ZettelGraph, ZettelC) ->
NeuronWebT t m ()
renderZettelContentCard (graph, zc) =
case zc of
Right z ->
renderZettelContent (evalAndRenderZettelQuery graph) z
Left z -> do
renderZettelRawContent z
renderZettelBottomPane :: DomBuilder t m => ZettelGraph -> Zettel -> NeuronWebT t m ()
renderZettelBottomPane graph z@Zettel {..} = do
let backlinks = nonEmpty $ G.backlinks isJust z graph
tags = nonEmpty zettelTags
when (isJust backlinks || isJust tags) $
elClass "nav" "ui bottom attached segment deemphasized" $
do
divClass "ui grid" $ do
divClass "fourteen wide column" $ do
whenJust backlinks $ \links -> do
elAttr "div" ("class" =: "ui header" <> "title" =: "Zettels that link here (branching or not)") $
text "Backlinks"
el "ul" $ do
forM_ links $ \(conn, zl) ->
el "li" $ Q.renderZettelLink Nothing (Just conn) def zl
whenJust tags $
divClass "two wide column" . renderTags
evalAndRenderZettelQuery ::
PandocBuilder t m =>
ZettelGraph ->
NeuronWebT t m [QueryError] ->
URILink ->
NeuronWebT t m [QueryError]
evalAndRenderZettelQuery graph oldRender uriLink@(URILink inner _uri) = do
case flip runReaderT (G.getZettels graph) (Q.runQueryURILink uriLink) of
Left e -> do
-- Error parsing or running the query.
fmap (e :) oldRender <* elInlineError e
Right Nothing -> do
-- This is not a query link; pass through.
oldRender
Right (Just res) -> do
Q.renderQueryResult inner res
pure mempty
where
elInlineError e =
elClass "span" "ui left pointing red basic label" $ do
text $ showQueryError e
renderZettelContent ::
forall t m a.
(PandocBuilder t m, Monoid a) =>
(NeuronWebT t m a -> URILink -> NeuronWebT t m a) ->
ZettelT Pandoc ->
NeuronWebT t m ()
renderZettelContent handleLink Zettel {..} = do
elClass "article" "ui raised attached segment zettel-content" $ do
unless zettelTitleInBody $ do
el "h1" $ text zettelTitle
void $ elPandoc (Config handleLink) zettelContent
whenJust zettelDate $ \date ->
divClass "metadata" $ do
elAttr "div" ("class" =: "date" <> "title" =: "Zettel date") $ do
elTime date
renderZettelRawContent :: (DomBuilder t m) => ZettelT Text -> m ()
renderZettelRawContent Zettel {..} = do
divClass "ui error message" $ do
elClass "h2" "header" $ text "Zettel failed to parse"
el "p" $ el "pre" $ text $ show zettelError
elClass "article" "ui raised attached segment zettel-content raw" $ do
el "pre" $ text $ zettelContent
renderTags :: DomBuilder t m => NonEmpty Tag -> NeuronWebT t m ()
renderTags tags = do
forM_ tags $ \t -> do
-- NOTE(ui): Ideally this should be at the top, not bottom. But putting it at
-- the top pushes the zettel content down, introducing unnecessary white
-- space below the title. So we put it at the bottom for now.
elAttr "span" ("class" =: "ui right ribbon label zettel-tag" <> "title" =: "Tag") $ do
neuronRouteLink
(Some $ Route_Search $ Just t)
( "class" =: "tag-inner"
<> "title" =: ("See all zettels tagged '" <> unTag t <> "'")
)
$ do
text $ unTag t
el "p" blank