diff --git a/CHANGELOG.md b/CHANGELOG.md index 81375fb..7e230cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Revision history for hablo +## 1.1.0.1 -- 2021-01-20 + +* Ensure compilation on Nix as far as 18.09 +* Fix missing metadata when landing on articles + ## 1.1.0.0 -- 2020-12-13 * Implement static pages diff --git a/hablo.cabal b/hablo.cabal index 438aab6..190cf2c 100644 --- a/hablo.cabal +++ b/hablo.cabal @@ -3,7 +3,7 @@ cabal-version: >= 1.10 -- For further documentation, see http://haskell.org/cabal/users-guide/ name: hablo -version: 1.1.0.0 +version: 1.1.0.1 synopsis: A minimalist static blog generator description: Hablo is a fediverse-oriented static blog generator for articles written @@ -50,15 +50,15 @@ library , Pretty , RSS -- other-extensions: - build-depends: aeson >= 1.4.0 && < 1.6 + build-depends: aeson >= 1.2.0 && < 1.6 , base >= 4.9.1 && < 4.15 , bytestring >= 0.10.8 && < 0.12 , containers >= 0.5.11 && < 0.7 , directory >= 1.3.1 && < 1.4 , filepath >= 1.4.2 && < 1.5 - , lucid >= 2.9.11 && < 2.10 + , lucid >= 2.8.0 && < 2.10 , mtl >= 2.2.2 && < 2.3 - , optparse-applicative >= 0.14.3 && < 0.17 + , optparse-applicative >= 0.14.0 && < 0.17 , parsec >= 3.1.13 && < 3.2 , template >= 0.2.0 && < 0.3 , text >= 1.2.3 && < 1.3 diff --git a/share/js/DomRenderer.js b/share/js/DomRenderer.js index 8b07ea6..8e9746e 100644 --- a/share/js/DomRenderer.js +++ b/share/js/DomRenderer.js @@ -7,15 +7,37 @@ import {defined} from UnitJS.Fun; return { articlesList: articlesList, + getResource: getResource, render: render, replaceMarkdown: replaceMarkdown }; +function getResource(url) { + var i = url.lastIndexOf('/'); + var path = url.slice(1, i); + if(path == blog.path.articlesPath) { + return {type: 'article', key: url.slice(i+1).replace(/\.html/, '')}; + } else if(path == blog.path.pagesPath) { + return {type: 'page', key: url.slice(i+1).replace(/\.html/, '')}; + } else if(path == '' || blog.tags[path] != undefined) { + var tag = path.length > 0 ? path : undefined; + return {type: 'list', tag: tag, all: url.slice(i+1) == 'all.html'}; + } else { + return {type: 'unknown'}; + } +} + +function resourceUrl(resource, limit) { + var directory = blog.path[resource.type + 'sPath']; + var extension = limit != undefined ? '.html' : '.md'; + return ["", directory, resource.key + extension].join('/'); +} + function replaceMarkdown() { var div = document.getElementById('contents'); if(div.children[0] && div.children[0].tagName.toLowerCase() == 'article') { - var contentType = window.location.pathname.slice(1).replace(/\/.*/, ''); - convertContent(contentType, div.children[0], true); + var resourceType = getResource(window.location.pathname).type; + convertContent(resourceType, div.children[0], true); } else { var articles = div.getElementsByClassName('articles')[0]; if(articles != undefined) { @@ -28,15 +50,15 @@ function replaceMarkdown() { } } -function convertContent(contentType, article, comments) { +function convertContent(resourceType, article, comments) { var header = article.getElementsByTagName('header')[0]; - if(contentType == 'article') { + if(resourceType == 'article') { header.appendChild(Metadata.get(article.id)); } var text = article.getElementsByTagName('pre')[0]; if(text != undefined) { article.replaceChild(getDiv(text.innerText), text); - if(contentType == 'article' && comments) { + if(resourceType == 'article' && comments) { Metadata.getComments(article.id) .forEach(article.appendChild.bind(article)); } @@ -59,59 +81,53 @@ function getDiv(markdown) { return d; } -function contentUrl(contentType, key, limit) { - var directory = blog.path[contentType + 'sPath']; - var extension = limit != undefined ? '.html' : '.md'; - return ["", directory, key + extension].join('/'); -} - -function commentsSection(contentType, key, limit) { - if(contentType != 'article' || limit != undefined) { +function commentsSection(resource, limit) { + if(resource.type != 'article' || limit != undefined) { return []; } else { - return Metadata.getComments(key); + return Metadata.getComments(resource.key); } } -function render(contentType, key, markdown, limit) { - var url = contentUrl(contentType, key, limit); - var resource = blog[contentType + 's'][key]; - var lines = markdown.split(/\n/).slice(resource.bodyOffset); +function render(resource, markdown, limit) { + var url = resourceUrl(resource, limit); + var content = blog[resource.type + 's'][resource.key]; + var lines = markdown.split(/\n/).slice(content.bodyOffset); var div = getDiv(lines.slice(0, limit).join('\n')); return Dom.make('article', {}, [ Dom.make('header', {}, [ Dom.make('h1', {}, [ - Dom.make('a', {href: url, innerText: resource.title}) - ])].concat(contentType == 'article' ? Metadata.get(key) : []) + Dom.make('a', {href: url, innerText: content.title}) + ])].concat(resource.type == 'article' ? Metadata.get(resource.key) : []) ), div - ].concat(commentsSection(contentType, key, limit))); + ].concat(commentsSection(resource, limit))); } -function pageTitle(tag, all) { - return Template.render(all ? 'allPage' : 'latestPage', {tag: tag}); +function pageTitle(resource) { + return Template.render(resource.all ? 'allPage' : 'latestPage', {tag: resource.tag}); } -function otherUrl(tag, all) { - var path = [tag, all ? '' : 'all.html']; +function otherUrl(resource) { + var path = [resource.tag, resource.all ? '' : 'all.html']; return '/' + path.filter(defined).join('/'); } -function articlesList(tag, all) { +function articlesList(resource) { return function(articlePreviews) { return [ - Dom.make('h2', {innerText: pageTitle(tag, all)}), - Dom.make('ul', {}, articlesListLinks(tag, all)), + Dom.make('h2', {innerText: pageTitle(resource)}), + Dom.make('ul', {}, articlesListLinks(resource)), Dom.make('div', {class: 'articles'}, articlePreviews.filter(defined)) ]; }; } -function articlesListLinks(tag, all) { +function articlesListLinks(resource) { var links = [ Dom.make('a', { - innerText: all ? blog.wording.latestLink : blog.wording.allLink, - href: otherUrl(tag, all), + innerText: resource.all ? blog.wording.latestLink : blog.wording.allLink, + href: otherUrl(resource), class: 'other' }) ]; @@ -120,7 +136,7 @@ function articlesListLinks(tag, all) { innerText: blog.wording.rssLink, href: 'rss.xml', class: 'RSS', - title: Template.render('rssTitle', {tag: tag}) + title: Template.render('rssTitle', {tag: resource.tag}) })); } return links.map(function(e) {return Dom.make('li', {}, [e]);}); diff --git a/share/js/Navigation.js b/share/js/Navigation.js index 61bd13b..d5ef373 100644 --- a/share/js/Navigation.js +++ b/share/js/Navigation.js @@ -1,4 +1,4 @@ -import {articlesList, render} from DomRenderer; +import {articlesList, getResource, render} from DomRenderer; import blog from Hablo.Config; import * as Async from UnitJS.Async; import * as Cache from UnitJS.Cache; @@ -60,23 +60,20 @@ function visit(url) { } function navigate(url) { - var path = decodeURI(url).split("/").slice(1); - if(blog.tags[path[0]] != undefined) { - show(getArticlesList(path[0], path[1] == "all.html")); - } else if(path[0] == blog.path.articlesPath) { - show(getResource('article', path[1].replace(/\.html$/, ''))); - } else if(path[0] == blog.path.pagesPath) { - show(getResource('page', path[1].replace(/\.html$/, ''))); - } else { - show(getArticlesList(null, path[0] == "all.html")); + var resource = getResource(url); + switch(resource.type) { + case 'list': show(getArticlesList(resource)); break; + case 'article': + case 'page': show(getCached(resource)); break; + default: console.log("No idea how to navigate to " + url); } } -function getResource(contentType, key) { +function getCached(resource) { return Async.bind( - cache[contentType].get(key), + cache[resource.type].get(resource.key), Async.map( - function(contents) {return [render(contentType, key, contents)];} + function(contents) {return [render(resource, contents)];} ) ); } @@ -86,23 +83,23 @@ function preview(key) { cache.article.get(key), function(contents) { return Async.wrap( - render('article', key, contents, blog.skin.previewLinesCount) + render({type: 'article', key: key}, contents, blog.skin.previewLinesCount) ); } ); } -function articleIds(tag, all) { - var ids = tag != undefined ? blog.tags[tag] : Object.keys(blog.articles); +function articleIds(resource) { + var ids = resource.tag != undefined ? blog.tags[resource.tag] : Object.keys(blog.articles); var reverseDate = function (id) {return -blog.articles[id].metadata.date;}; ids.sort(Fun.compare(reverseDate)); - return ids.slice(0, all ? undefined : blog.skin.previewArticlesCount); + return ids.slice(0, resource.all ? undefined : blog.skin.previewArticlesCount); } -function getArticlesList(tag, all) { +function getArticlesList(resource) { return Async.bind( - Async.parallel.apply(null, articleIds(tag, all).map(preview)), - Async.map(articlesList(tag, all)) + Async.parallel.apply(null, articleIds(resource).map(preview)), + Async.map(articlesList(resource)) ); }