Added opengraph/meta tags
This commit is contained in:
parent
981bc757e4
commit
f88c5b46fc
8 changed files with 160 additions and 13 deletions
|
@ -1,7 +1,7 @@
|
||||||
import gleam/option
|
import gleam/option
|
||||||
import gloss/paths.{type PathConfiguration}
|
import gloss/paths.{type PathConfiguration}
|
||||||
import gloss/rendering/views.{
|
import gloss/rendering/views.{
|
||||||
type BaseView, type FeedView, type ListPageView, type PageView,
|
type BaseView, type FeedView, type ListPageView, type MetaView, type PageView,
|
||||||
type SinglePostView,
|
type SinglePostView,
|
||||||
}
|
}
|
||||||
import gloss/compiler.{type CompileDatabase, type Compiler}
|
import gloss/compiler.{type CompileDatabase, type Compiler}
|
||||||
|
@ -13,6 +13,7 @@ import gloss/rendering/database.{type RenderDatabase} as _
|
||||||
pub type Views {
|
pub type Views {
|
||||||
Views(
|
Views(
|
||||||
base: fn(Database, Configuration) -> BaseView,
|
base: fn(Database, Configuration) -> BaseView,
|
||||||
|
meta: fn(Database, Configuration) -> MetaView,
|
||||||
single_post_full: fn(Database, Configuration) -> SinglePostView,
|
single_post_full: fn(Database, Configuration) -> SinglePostView,
|
||||||
single_post_list: fn(Database, Configuration) -> SinglePostView,
|
single_post_list: fn(Database, Configuration) -> SinglePostView,
|
||||||
page: fn(Database, Configuration) -> PageView,
|
page: fn(Database, Configuration) -> PageView,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import gloss/rendering/views/base
|
||||||
import gloss/rendering/views/list_page
|
import gloss/rendering/views/list_page
|
||||||
import gloss/rendering/views/page
|
import gloss/rendering/views/page
|
||||||
import gloss/rendering/views/feed
|
import gloss/rendering/views/feed
|
||||||
|
import gloss/rendering/views/meta
|
||||||
import gloss/renderer
|
import gloss/renderer
|
||||||
import gloss/paths
|
import gloss/paths
|
||||||
import gloss/parser
|
import gloss/parser
|
||||||
|
@ -12,6 +13,7 @@ import gloss/compiler
|
||||||
|
|
||||||
const default_views = config.Views(
|
const default_views = config.Views(
|
||||||
base: base.generate,
|
base: base.generate,
|
||||||
|
meta: meta.generate,
|
||||||
single_post_full: single_post.full_view,
|
single_post_full: single_post.full_view,
|
||||||
single_post_list: single_post.list_view,
|
single_post_list: single_post.list_view,
|
||||||
page: page.generate,
|
page: page.generate,
|
||||||
|
|
|
@ -61,3 +61,10 @@ pub fn comparator(a: Post, b: Post) -> Order {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_iso8601(post: Post) -> String {
|
||||||
|
case post.date {
|
||||||
|
JustDate(d) -> date.format_iso(d)
|
||||||
|
DateTime(luxon: l, ..) -> luxon.to_iso(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import gleam/result
|
||||||
import gleam/int
|
import gleam/int
|
||||||
import lustre/element.{type Element}
|
import lustre/element.{type Element}
|
||||||
import gloss/rendering/views.{
|
import gloss/rendering/views.{
|
||||||
type BaseView, type FeedView, type ListPageView, type PageView,
|
type BaseView, type FeedView, type ListPageView, type MetaView, type PageView,
|
||||||
type SinglePostView, ListInfo,
|
type SinglePostView, ListInfo,
|
||||||
}
|
}
|
||||||
import gloss/rendering/database.{
|
import gloss/rendering/database.{
|
||||||
|
@ -22,6 +22,7 @@ import gloss/utils/date
|
||||||
pub type Views {
|
pub type Views {
|
||||||
Views(
|
Views(
|
||||||
base: BaseView,
|
base: BaseView,
|
||||||
|
meta: MetaView,
|
||||||
single_post_full: SinglePostView,
|
single_post_full: SinglePostView,
|
||||||
page: PageView,
|
page: PageView,
|
||||||
list_page: ListPageView,
|
list_page: ListPageView,
|
||||||
|
@ -37,6 +38,7 @@ pub fn render(
|
||||||
let views =
|
let views =
|
||||||
Views(
|
Views(
|
||||||
base: config.rendering.views.base(db, config),
|
base: config.rendering.views.base(db, config),
|
||||||
|
meta: config.rendering.views.meta(db, config),
|
||||||
single_post_full: config.rendering.views.single_post_full(db, config),
|
single_post_full: config.rendering.views.single_post_full(db, config),
|
||||||
page: config.rendering.views.page(db, config),
|
page: config.rendering.views.page(db, config),
|
||||||
list_page: config.rendering.views.list_page(db, config),
|
list_page: config.rendering.views.list_page(db, config),
|
||||||
|
@ -76,7 +78,11 @@ pub fn render_posts(
|
||||||
|> list.map(fn(post_with_id) {
|
|> list.map(fn(post_with_id) {
|
||||||
let assert Ok(content) = dict.get(post_contents, post_with_id.id)
|
let assert Ok(content) = dict.get(post_contents, post_with_id.id)
|
||||||
let rendered =
|
let rendered =
|
||||||
views.base(views.single_post_full(content), post_with_id.post.title)
|
views.base(
|
||||||
|
views.single_post_full(content),
|
||||||
|
views.meta(views.Post(post_with_id.post)),
|
||||||
|
post_with_id.post.title,
|
||||||
|
)
|
||||||
RenderedSinglePost(post_with_id.post, rendered)
|
RenderedSinglePost(post_with_id.post, rendered)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -87,7 +93,12 @@ pub fn render_pages(
|
||||||
views: Views,
|
views: Views,
|
||||||
) {
|
) {
|
||||||
list.map(compiled_pages, fn(page) {
|
list.map(compiled_pages, fn(page) {
|
||||||
let rendered = views.base(views.page(page), page.orig.title)
|
let rendered =
|
||||||
|
views.base(
|
||||||
|
views.page(page),
|
||||||
|
views.meta(views.Page(page.orig)),
|
||||||
|
page.orig.title,
|
||||||
|
)
|
||||||
RenderedPage(page.orig, rendered)
|
RenderedPage(page.orig, rendered)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -233,7 +244,12 @@ fn pageify_posts(
|
||||||
extra_header: extra_header,
|
extra_header: extra_header,
|
||||||
)
|
)
|
||||||
|
|
||||||
let page_content = views.base(views.list_page(info), title_prefix)
|
let page_content =
|
||||||
|
views.base(
|
||||||
|
views.list_page(info),
|
||||||
|
views.meta(views.Other(title_prefix, "")),
|
||||||
|
title_prefix,
|
||||||
|
)
|
||||||
ListPage(page: page, content: page_content)
|
ListPage(page: page, content: page_content)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,19 @@
|
||||||
import lustre/element.{type Element}
|
import lustre/element.{type Element}
|
||||||
import gloss/compiler.{type CompiledPage, type CompiledPost}
|
import gloss/compiler.{type CompiledPage, type CompiledPost}
|
||||||
|
import gloss/models/post
|
||||||
|
import gloss/models/page
|
||||||
|
|
||||||
pub type BaseView =
|
pub type BaseView =
|
||||||
fn(Element(Nil), String) -> Element(Nil)
|
fn(Element(Nil), List(Element(Nil)), String) -> Element(Nil)
|
||||||
|
|
||||||
|
pub type PageType {
|
||||||
|
Post(post.Post)
|
||||||
|
Page(page.Page)
|
||||||
|
Other(title: String, description: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type MetaView =
|
||||||
|
fn(PageType) -> List(Element(Nil))
|
||||||
|
|
||||||
pub type SinglePostView =
|
pub type SinglePostView =
|
||||||
fn(CompiledPost) -> Element(Nil)
|
fn(CompiledPost) -> Element(Nil)
|
||||||
|
|
|
@ -26,8 +26,8 @@ pub type PreRendered {
|
||||||
|
|
||||||
pub fn generate(db: Database, config: Configuration) {
|
pub fn generate(db: Database, config: Configuration) {
|
||||||
let pre_rendered = pre_render(db, config)
|
let pre_rendered = pre_render(db, config)
|
||||||
fn(inner: Element(Nil), title_prefix: String) {
|
fn(inner: Element(Nil), extra_meta: List(Element(Nil)), title_prefix: String) {
|
||||||
view(db, config, pre_rendered, inner, title_prefix)
|
view(db, config, pre_rendered, inner, extra_meta, title_prefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ fn view(
|
||||||
config: Configuration,
|
config: Configuration,
|
||||||
pre_rendered: PreRendered,
|
pre_rendered: PreRendered,
|
||||||
inner: Element(Nil),
|
inner: Element(Nil),
|
||||||
|
extra_meta: List(Element(Nil)),
|
||||||
title_prefix: String,
|
title_prefix: String,
|
||||||
) {
|
) {
|
||||||
let title_text = case title_prefix {
|
let title_text = case title_prefix {
|
||||||
|
@ -71,6 +72,7 @@ fn view(
|
||||||
option.Some(url) -> link([rel("me"), value(url)])
|
option.Some(url) -> link([rel("me"), value(url)])
|
||||||
_ -> element.none()
|
_ -> element.none()
|
||||||
},
|
},
|
||||||
|
..extra_meta
|
||||||
]),
|
]),
|
||||||
body([], [
|
body([], [
|
||||||
header([id("title"), role("banner")], [
|
header([id("title"), role("banner")], [
|
||||||
|
|
|
@ -11,7 +11,6 @@ import gloss/models/post
|
||||||
import gloss/config.{type Configuration}
|
import gloss/config.{type Configuration}
|
||||||
import gloss/compiler.{type CompiledPost}
|
import gloss/compiler.{type CompiledPost}
|
||||||
import gloss/utils/luxon
|
import gloss/utils/luxon
|
||||||
import gloss/utils/date
|
|
||||||
|
|
||||||
pub fn generate(_db: Database, config: Configuration) {
|
pub fn generate(_db: Database, config: Configuration) {
|
||||||
fn(posts: List(CompiledPost)) {
|
fn(posts: List(CompiledPost)) {
|
||||||
|
@ -43,10 +42,7 @@ pub fn generate(_db: Database, config: Configuration) {
|
||||||
),
|
),
|
||||||
rights(config.rendering.copyright),
|
rights(config.rendering.copyright),
|
||||||
..list.map(posts, fn(post) {
|
..list.map(posts, fn(post) {
|
||||||
let date_str = case post.orig.date {
|
let date_str = post.to_iso8601(post.orig)
|
||||||
post.JustDate(d) -> date.format_iso(d)
|
|
||||||
post.DateTime(luxon: l, ..) -> luxon.to_iso(l)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry([
|
entry([
|
||||||
title(post.orig.title),
|
title(post.orig.title),
|
||||||
|
|
112
src/gloss/rendering/views/meta.gleam
Normal file
112
src/gloss/rendering/views/meta.gleam
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
import gleam/dict
|
||||||
|
import gleam/list
|
||||||
|
import gleam/option
|
||||||
|
import gleam/result
|
||||||
|
import lustre/element/html
|
||||||
|
import lustre/attribute
|
||||||
|
import gloss/rendering/views.{type PageType, Other, Page, Post}
|
||||||
|
import gloss/models/post
|
||||||
|
import gloss/models/page
|
||||||
|
import gloss/config.{type Configuration}
|
||||||
|
import gloss/models/database.{type Database}
|
||||||
|
|
||||||
|
pub fn generate(_db: Database, config: Configuration) {
|
||||||
|
fn(page_type: PageType) {
|
||||||
|
list.flatten([
|
||||||
|
[
|
||||||
|
name_meta("author", config.author.name),
|
||||||
|
name_meta("description", description(page_type)),
|
||||||
|
// Schema.org
|
||||||
|
itemprop_meta("name", title(page_type)),
|
||||||
|
itemprop_meta("author", config.author.name),
|
||||||
|
itemprop_meta("headline", title(page_type)),
|
||||||
|
itemprop_meta("description", description(page_type)),
|
||||||
|
// Twitter/X card
|
||||||
|
name_meta("twitter:card", "summary_large_image"),
|
||||||
|
name_meta("twitter:site", ""),
|
||||||
|
name_meta("twitter:title", title(page_type)),
|
||||||
|
name_meta("twitter:description", description(page_type)),
|
||||||
|
name_meta("twitter:creator", ""),
|
||||||
|
// Open Graph
|
||||||
|
property_meta("og:title", title(page_type)),
|
||||||
|
property_meta("og:type", case page_type {
|
||||||
|
Post(..) | Page(..) -> "article"
|
||||||
|
_ -> "website"
|
||||||
|
}),
|
||||||
|
property_meta("og:description", description(page_type)),
|
||||||
|
property_meta("og:site_name", config.blog_name),
|
||||||
|
],
|
||||||
|
case page_type {
|
||||||
|
Post(post) -> {
|
||||||
|
let timestamp = post.to_iso8601(post)
|
||||||
|
|
||||||
|
let image =
|
||||||
|
post.headers
|
||||||
|
|> dict.from_list()
|
||||||
|
|> dict.get("image")
|
||||||
|
|> option.from_result()
|
||||||
|
|
||||||
|
[
|
||||||
|
property_meta("article:published_time", timestamp),
|
||||||
|
itemprop_meta("datePublished", timestamp),
|
||||||
|
property_meta(
|
||||||
|
"og:url",
|
||||||
|
config.blog_url <> config.paths.single_post(post),
|
||||||
|
),
|
||||||
|
..case image {
|
||||||
|
option.Some(image) -> [
|
||||||
|
name_meta("twitter:image", image),
|
||||||
|
property_meta("og:image", image),
|
||||||
|
itemprop_meta("image", image),
|
||||||
|
]
|
||||||
|
option.None -> []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
Page(page) -> [
|
||||||
|
property_meta("og:url", config.blog_url <> config.paths.page(page)),
|
||||||
|
]
|
||||||
|
_ -> []
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name_meta(name: String, content: String) {
|
||||||
|
meta("name", name, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn itemprop_meta(itemprop: String, content: String) {
|
||||||
|
meta("itemprop", itemprop, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn property_meta(property: String, content: String) {
|
||||||
|
meta("property", property, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn meta(attr_name: String, attr_value: String, content: String) {
|
||||||
|
html.meta([
|
||||||
|
attribute.attribute(attr_name, attr_value),
|
||||||
|
attribute.attribute("content", content),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(page_type: PageType) {
|
||||||
|
case page_type {
|
||||||
|
Post(post.Post(headers: headers, ..))
|
||||||
|
| Page(page.Page(headers: headers, ..)) ->
|
||||||
|
headers
|
||||||
|
|> dict.from_list()
|
||||||
|
|> dict.get("description")
|
||||||
|
|> result.unwrap("")
|
||||||
|
Other(description: description, ..) -> description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn title(page_type: PageType) {
|
||||||
|
case page_type {
|
||||||
|
Post(post.Post(title: title, ..))
|
||||||
|
| Page(page.Page(title: title, ..))
|
||||||
|
| Other(title: title, ..) -> title
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue