diff --git a/js/blogtini.js b/js/blogtini.js index 5ae70b9..3a1cbc0 100644 --- a/js/blogtini.js +++ b/js/blogtini.js @@ -251,6 +251,7 @@ async function main() { /* eg: deno run -A js/blogtini.js index.html */ + if (!Deno.args.length) return const fi = Deno.args[0] const body = Deno.readTextFileSync(fi) if (!body.startsWith(HEADLINE)) { @@ -621,37 +622,29 @@ function storage_add(post) { // xxx use snippet } -async function comments_markup(path) { +/** + * @param {string} path + */ +async function comments_get(path) { let posts_with_comments try { posts_with_comments = (await fetcher(`${state.top_dir}comments/index.txt`))?.split('\n') /* eslint-disable-next-line no-empty */ // deno-lint-ignore no-empty } catch {} - if (!posts_with_comments?.includes(path)) return null - - const comments = (await fetcher(`${state.top_dir}comments/${path}/index.json`))?.filter((e) => Object.keys(e).length) - - const refId = 'xxx' - - return comments.map((e) => ` -
-
- ${e.name}'s Gravatar -
- - Reply -
-
-
- ${e.body} -
-
`).join('') + if (!posts_with_comments?.includes(path)) + return [] + + // oldest comments (or oldest in a thread) first + return (await fetcher(`${state.top_dir}comments/${path}/index.json`))?.filter((e) => Object.keys(e).length).sort((a, b) => a.date < b.date).map((e) => { + // delete any unused keys in each comment hashmap + for (const [k, v] of Object.entries(e)) { + if (v === '' || v === undefined || v === null) + delete e[k] + if (!['id', 'name', 'email', 'date', 'website', 'replyID', 'body'].includes(k)) + delete e[k] + } + return e + }) } @@ -696,7 +689,7 @@ function create_comment_form(entryId, comments) {

Comments

- ${comments ?? '

Nothing yet.

'} + ${comments && comments.length ? '' : '

Nothing yet.

'}
` @@ -971,7 +964,7 @@ export { cssify, datetime, markdown_to_html, - comments_markup, + comments_get, create_comment_form, share_buttons, dark_mode, diff --git a/theme/future-imperfect/bt-comment.js b/theme/future-imperfect/bt-comment.js new file mode 100644 index 0000000..e92e4b2 --- /dev/null +++ b/theme/future-imperfect/bt-comment.js @@ -0,0 +1,60 @@ +import { LitElement, html, css } from 'https://esm.archive.org/lit' +import { cssify, datetime } from '../../js/blogtini.js' +import { + css_post, css_dark, css_footer, css_title, css_buttons, +} from './index.js' + + +customElements.define('bt-comment', class extends LitElement { + static get properties() { + return { + id: { type: String }, + name: { type: String }, + email: { type: String }, + date: { type: String }, + website: { type: String }, + replyID: { type: String }, + body: { type: String }, + } + } + + render() { + return html` + + +
+
+ ${this.name}'s Gravatar +
+ + Reply +
+
+
+ ${this.body} + +
+
+ + ` + } + + static get styles() { + return [ + css_post(), // xxxcc figure these out + css_title(), + css_footer(), + css_buttons(), + css_dark(), + ] + } +}) diff --git a/theme/future-imperfect/bt-post-full.js b/theme/future-imperfect/bt-post-full.js index 926abf1..50ccb19 100644 --- a/theme/future-imperfect/bt-post-full.js +++ b/theme/future-imperfect/bt-post-full.js @@ -3,7 +3,7 @@ import { LitElement, html } from 'https://esm.archive.org/lit' import { url2post, cssify, - markdown_to_html, comments_markup, create_comment_form, + markdown_to_html, comments_get, create_comment_form, share_buttons, } from '../../js/blogtini.js' import { @@ -16,6 +16,7 @@ customElements.define('bt-post-full', class extends LitElement { return { url: { type: String }, comments_form: { type: String }, + comments: { type: Object }, } } @@ -23,14 +24,14 @@ customElements.define('bt-post-full', class extends LitElement { const post = url2post(this.url) const body = markdown_to_html(post.body_raw) - // use a default base in case url is relative (pathname) only - const key = new URL(post.url, 'https://blogtini.com').pathname.replace(/^\/+/, '').replace(/\/+$/, '') // xxx - // console.error({key}) - - if (post.type === 'post') { - comments_markup(key).then( - (comments_htm) => { - this.comments_form = create_comment_form(post.url, comments_htm) + if (post.type === 'post' && !this.comments_form) { + // use a default base in case url is relative (pathname) only + const key = new URL(post.url, 'https://blogtini.com').pathname.replace(/^\/+/, '').replace(/\/+$/, '') // xxx + // console.error({key}) + comments_get(key).then( + (comments) => { + this.comments_form = create_comment_form(post.url, comments) + this.comments = comments }, ) } @@ -81,6 +82,9 @@ customElements.define('bt-post-full', class extends LitElement { updated() { + if (this.comments && this.comments.length) + this.comments_insert() + // add code highlighting const codes = this.shadowRoot.querySelectorAll('pre code') if (codes.length) { @@ -90,6 +94,46 @@ customElements.define('bt-post-full', class extends LitElement { } } + /** + * Cleverly use DOM to add (potentially nested) comment elements into a div + * -- because threading replies (and replies of replies) gets complicated suuuuper quick + */ + comments_insert() { + // loop over comments, appending each into the right parent, until all are added + // (or no more addable if corrupted / invalid parent pointer) + // eslint-disable-next-line no-empty + while (this.comments.reduce((sum, e) => sum + this.comment_insert(e), 0)) {} + + for (const com of this.comments) { + // eslint-disable-next-line no-console + if (com.id) console.error('Comment orphaned', com) + } + } + + /** + * Adds a comment into DOM, finding the right parent for threaded comments, etc. + * @param {object} com + * @returns {number} 0 or 1 comments added + */ + comment_insert(com) { + if (!com.id) return 0 + const e = document.createElement('bt-comment') + for (const [k, v] of Object.entries(com)) + e.setAttribute(k, v) // xxxcc JSON.stringify(v)) + + const addto = com.replyID + ? this.shadowRoot.getElementById(com.replyID) + : this.shadowRoot.querySelector('.comments-container') + if (!addto) + return 0 // *should* never happen -- but cant find parent for this comment's `replyID` + + addto.appendChild(e) + + // eslint-disable-next-line no-param-reassign + delete com.id + return 1 + } + static get styles() { return [ diff --git a/theme/future-imperfect/index.js b/theme/future-imperfect/index.js index 55fc170..f28e6f7 100644 --- a/theme/future-imperfect/index.js +++ b/theme/future-imperfect/index.js @@ -9,6 +9,7 @@ import './bt-post-mini.js' import './bt-post-header.js' import './featured-image.js' import './post-stats.js' +import './bt-comment.js' function css_buttons() {