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) => `
-`).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) {
`
@@ -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`
+
+
+
+
+ `
+ }
+
+ 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() {
${e.name}
- - ${'' /* eslint-disable-next-line no-use-before-define */} - - -