diff --git a/js/comments.js b/js/comments.js new file mode 100644 index 0000000..e8381da --- /dev/null +++ b/js/comments.js @@ -0,0 +1,69 @@ +function Comments(modules) { + var comments = modules.cache.make(function(threadId) { + return modules.async.bind( + modules.async.http({method: 'GET', url: url(threadId)}), + function(queryResult) { + if(queryResult.status == 200) { + try { + return modules.async.wrap(render(JSON.parse(queryResult.responseText))); + } catch(e) { + return modules.async.fail('Server returned invalid JSON for ' + url); + } + } else { + return modules.async.fail('Could not load comments at ' + url); + } + } + ); + }) + return { + get: get + }; + + function url(threadId) { + return blog.path.commentsAt + '/api/v1/statuses/' + threadId + '/context'; + } + + function get(articleKey) { + var threadId = blog.articles[articleKey].metadata.comments; + if(blog.path.commentsAt != undefined && threadId != undefined) { + var ul = modules.dom.make('ul'); + modules.async.run( + modules.async.bind( + comments.get(threadId), + modules.async.map(function(comments) { + comments.forEach(function(comment) {ul.appendChild(comment);}); + }) + ) + ); + return emptySection(ul, threadId); + } else { + return []; + } + } + + function emptySection(ul, threadId) { + return [modules.dom.make('div', {class: 'comments'}, [ + modules.dom.make('h2', {innerText: 'Comments'}), + ul, + modules.dom.make('a', { + href: blog.path.commentsAt + '/notice/' + threadId, + innerText: 'Comment on the fediverse' + }) + ])]; + } + + function render(comments) { + return comments.descendants.map(function(descendant) { + return modules.dom.make('li', {}, [ + modules.dom.make('cite', {}, [ + modules.dom.make('a', { + href: descendant.account.url, + innerText: descendant.account.username + }), + modules.dom.make('p', {innerText: descendant.created_at}), + modules.dom.make('div', {innerHTML: descendant.content}) + ]) + ]); + }); + } +} diff --git a/js/domRenderer.js b/js/domRenderer.js index 3dff886..0fb9452 100644 --- a/js/domRenderer.js +++ b/js/domRenderer.js @@ -8,7 +8,8 @@ function DomRenderer(modules) { function replaceMarkdown() { var div = document.getElementById('contents'); if(div.children[0] && div.children[0].tagName.toLowerCase() == 'article') { - convertArticle(div.children[0]); + var re = new RegExp('/' + blog.path.articlesPath + '/([^.]+)\.html'); + convertArticle(div.children[0], document.location.pathname.replace(re, '$1')); } else { var articles = div.getElementsByClassName('articles')[0]; if(articles != undefined) { @@ -21,11 +22,14 @@ function DomRenderer(modules) { } } - function convertArticle(article) { + function convertArticle(article, key) { var header = article.getElementsByTagName('header')[0]; var text = article.getElementsByTagName('pre')[0]; if(text != undefined) { article.replaceChild(getDiv(text.innerText), text); + if(key != undefined) { + article.appendChild(modules.comments.get(key)[0]); + } } else { console.log('No content found for this article'); } @@ -56,7 +60,7 @@ function DomRenderer(modules) { ]) ]), div - ]); + ].concat(limit != undefined ? [] : modules.comments.get(key))); } function pageTitle(tag, all) { diff --git a/js/main.js b/js/main.js index 8a3e8d2..76f9ec3 100644 --- a/js/main.js +++ b/js/main.js @@ -5,7 +5,8 @@ window.addEventListener('load', function() { var fun = unitJS.Fun(); var md = new Remarkable({html: true}); md.block.ruler.enable(['footnote']); - var domRenderer = DomRenderer({fun: fun, md: md, dom: dom}); + var comments = Comments({async: async, cache: cache, dom: dom}); + var domRenderer = DomRenderer({comments: comments, fun: fun, md: md, dom: dom}); var navigation = Navigation({async: async, cache: cache, fun: fun, md: md, dom: dom, domRenderer: domRenderer}); domRenderer.replaceMarkdown(); navigation.hijackLinks(); diff --git a/src/Arguments.hs b/src/Arguments.hs index b83b14c..6147b5d 100644 --- a/src/Arguments.hs +++ b/src/Arguments.hs @@ -18,6 +18,7 @@ data Arguments = BlogConfig { , articlesPath :: FilePath , bannerPath :: Maybe FilePath , cardImage :: Maybe FilePath + , commentsAt :: Maybe String , favicon :: Maybe FilePath , headPath :: Maybe FilePath , name :: Maybe String @@ -49,6 +50,7 @@ blogConfig = BlogConfig ) <*> option filePath 'b' "banner" "BANNER_PATH" "path to the file to use for the blog's banner" <*> option filePath 'c' "card-image" "CARD_IMAGE" "path to the image to use for the blog's card" + <*> option filePath 'C' "comments-at" "INSTANCE_URL" "url of the instance where comments are stored" <*> option filePath 'f' "favicon" "FAVICON" "path to the image to use for the blog's favicon" <*> option filePath 'H' "head" "HEAD_PATH" "path to the file to add in the blog's head" <*> option str 'n' "name" "BLOG_NAME" "name of the blog" diff --git a/src/Blog/Path.hs b/src/Blog/Path.hs index d643c80..703d9d6 100644 --- a/src/Blog/Path.hs +++ b/src/Blog/Path.hs @@ -8,6 +8,7 @@ import qualified Arguments as Arguments (Arguments(..)) data Path = Path { articlesPath :: FilePath + , commentsAt :: Maybe String , pagesPath :: Maybe FilePath , root :: FilePath } @@ -15,6 +16,7 @@ data Path = Path { build :: Arguments -> Path build arguments = Path { articlesPath = Arguments.articlesPath arguments + , commentsAt = Arguments.commentsAt arguments , pagesPath = Arguments.pagesPath arguments , root = Arguments.sourceDir arguments } diff --git a/src/JSON.hs b/src/JSON.hs index 9ac9fb5..1dbe427 100644 --- a/src/JSON.hs +++ b/src/JSON.hs @@ -28,6 +28,7 @@ instance ToJSON ArticleExport where data PathExport = PathExport { articlesPath :: FilePath + , commentsAt :: Maybe String , pagesPath :: Maybe FilePath } deriving (Generic) @@ -67,6 +68,7 @@ exportBlog = do articles = mapWithKey (export blog) $ Blog.articles blog , path = PathExport { articlesPath = Blog.articlesPath $ Blog.path blog + , commentsAt = Blog.commentsAt $ Blog.path blog , pagesPath = Blog.pagesPath $ Blog.path blog } , skin = SkinExport {