website/frontend/src/Pages/Blog.elm
2024-12-19 01:43:02 -06:00

320 lines
8.5 KiB
Elm
Executable file

module Pages.Blog exposing (Model, Msg, page)
import Config.Data.Identity exposing (pageNames)
import Config.Helpers.Articles.Article exposing (extractFirstWords)
import Config.Helpers.CardFormat
exposing
( cardContentSpacing
, cardFormatter
, cardMaker
, cardTitleMaker
, desktopCardMaker
, desktopImageBoxSize
, desktopImageSize
, fieldSpacer
, mobileCardMaker
, mobileImageBoxSize
, mobileImageSize
, topLevelBox
, underConstructionMaker
)
import Config.Helpers.Converters
exposing
( formatName
, wordCount
)
import Config.Helpers.Format
exposing
( headerFontSizeSmall
, paragraphFontSize
, paragraphSpacing
)
import Config.Helpers.Headers.Header exposing (headerMaker)
import Config.Helpers.Headers.Types exposing (Header)
import Config.Helpers.Markdown
exposing
( renderDeviceMarkdown
, renderDeviceMarkdownNoToc
)
import Config.Helpers.Response
exposing
( pageList
, topLevelContainer
)
import Config.Helpers.Viewport exposing (resetViewport)
import Config.Pages.Blog.Records.BigFatSurprise exposing (articleBigFatSurprise)
import Config.Pages.Blog.Records.EverettVegans exposing (articleEverettVegans)
import Config.Pages.Blog.Records.HunterGatherers exposing (articleHunterGatherers)
import Config.Pages.Blog.Records.MeatApologetics exposing (articleMeatApologetics)
import Config.Pages.Blog.Records.NagraGoodrich exposing (articleNagraGoodrich)
import Config.Pages.Blog.Records.PlantBasedMeta exposing (articlePlantBasedMeta)
import Config.Pages.Blog.Records.QuackSmashing exposing (articleQuackSmashing)
import Config.Pages.Blog.Records.SapienDiet exposing (articleSapienDiet)
import Config.Pages.Blog.Records.SeedOils exposing (articleSeedOils)
import Config.Pages.Blog.Records.Shenangians exposing (articleShenanigans)
import Config.Pages.Blog.Records.SweetDeception exposing (articleSweetDeception)
import Config.Pages.Blog.Types exposing (..)
import Config.Style.Colour as T exposing (..)
import Config.Style.Icons.Icons exposing (construction)
import Effect exposing (Effect)
import Element as E exposing (..)
import Element.Background as B
import Element.Border as D
import Element.Font as F
import Html.Attributes as H exposing (style)
import Layouts
import Page exposing (Page)
import Route exposing (Route)
import Route.Path as Path
import Shared exposing (..)
import View exposing (View)
page : Shared.Model -> Route () -> Page Model Msg
page shared route =
Page.new
{ init = init
, update = update
, subscriptions = subscriptions
, view = view shared
}
|> Page.withLayout toLayout
toLayout : Model -> Layouts.Layout Msg
toLayout model =
Layouts.Navbar {}
-- INIT
type alias Model =
{}
init : () -> ( Model, Effect Msg )
init () =
( {}
, Effect.map
(\_ -> NoOp)
(Effect.sendCmd resetViewport)
)
-- UPDATE
type Msg
= NoOp
update : Msg -> Model -> ( Model, Effect Msg )
update msg model =
case msg of
NoOp ->
( model
, Effect.none
)
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
-- VIEW
view : Shared.Model -> Model -> View Msg
view shared model =
{ title = pageNames.pageHyperBlog
, attributes = []
, element = blogContainer shared.device
}
blogContainer : Device -> Element msg
blogContainer device =
topLevelContainer (blogList device)
blogList : Device -> Element msg
blogList device =
column
(case ( device.class, device.orientation ) of
_ ->
pageList
)
<|
List.concat
[ List.map headerMaker
[ blogHeader ]
, (case ( device.class, device.orientation ) of
( Phone, Portrait ) ->
List.map mobileBlogMaker
( Tablet, Portrait ) ->
List.map mobileBlogMaker
_ ->
List.map desktopBlogMaker
)
[ articleShenanigans
, articleSweetDeception
, articleEverettVegans
, articleQuackSmashing
, articleSapienDiet
, articleNagraGoodrich
, articleMeatApologetics
, articleSeedOils
, articleHunterGatherers
, articlePlantBasedMeta
, articleBigFatSurprise
]
]
blogHeader : Header
blogHeader =
let
name =
"Blog"
in
{ headerTitle = String.toUpper name
, headerBody = "This page features blog articles written by me, along with contributions from guest authors, covering topics primarily related to nutrition science, health science, and debate."
}
desktopBlogMaker : BlogArticle -> Element msg
desktopBlogMaker article =
row
topLevelBox
[ desktopCardMaker desktopImageBoxSize desktopImageSize (articleImage article) article.articleLink
, cardMaker
[ cardTitleMaker article.articleName
, cardFormatter
[ cardContentSpacing
[ column
fieldSpacer
[ articleMaker article ]
]
]
]
]
mobileBlogMaker : BlogArticle -> Element msg
mobileBlogMaker article =
row
topLevelBox
[ column [] []
, cardMaker
[ cardTitleMaker article.articleName
, cardFormatter
[ cardContentSpacing
[ column
fieldSpacer
[ row [ width fill, spacing 10 ]
[ mobileCardMaker mobileImageBoxSize mobileImageSize (articleImage article) article.articleLink
, column
[ width fill ]
[ articleMaker article ]
]
]
]
]
]
]
articleImage :
BlogArticle
->
{ src : String
, description : String
}
articleImage article =
{ src = "blog/" ++ article.articleImage ++ "thumb.png"
, description = article.articleName
}
articleMaker : BlogArticle -> Element msg
articleMaker article =
column
[ E.width fill
, centerX
]
[ column [ width fill ]
(articleRows article
++ [ row []
[ paragraph
[ F.color colourTheme.textLightGrey
, paragraphSpacing
, paragraphFontSize
, spacing 3
, F.regular
, F.alignLeft
, paddingEach
{ top = 8
, bottom = 0
, left = 0
, right = 0
}
]
[ renderDeviceMarkdownNoToc (extractFirstWords article.articleBody) ]
]
]
)
]
infoRow : String -> String -> Element msg
infoRow label value =
row [ width fill ]
[ el [ width <| px 88 ] <|
el
[ F.color colourTheme.textLightOrange
, paragraphSpacing
, F.bold
, headerFontSizeSmall
, E.width fill
]
<|
text label
, el [ width fill ] <|
paragraph
[ F.color colourTheme.textLightGrey
, F.regular
, paragraphFontSize
, width fill
]
[ text value ]
]
articleRows : BlogArticle -> List (Element msg)
articleRows article =
let
referenceCount =
List.length article.articleReferences
in
[ infoRow "Published:" article.articlePublished
, infoRow "Author:" article.articleAuthor
, infoRow "Duration:" (String.fromInt (wordCount article.articleBody // 225) ++ " minutes")
, infoRow "Words:" (String.fromInt (wordCount article.articleBody))
]
++ (if referenceCount >= 2 then
[ infoRow "Sources:" (String.fromInt referenceCount) ]
else
[]
)