Files
log/src/components/posts.ts
2026-01-16 00:52:15 +09:00

118 lines
4.2 KiB
TypeScript

import type { BlogPost } from '../types.js'
import { putRecord } from '../lib/auth.js'
import { renderMarkdown } from '../lib/markdown.js'
import { escapeHtml } from '../lib/utils.js'
import { renderDiscussionLink, loadDiscussionPosts, getAppUrl } from './discussion.js'
function formatDate(dateStr: string): string {
const date = new Date(dateStr)
return date.toLocaleDateString('ja-JP', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
})
}
export function mountPostList(container: HTMLElement, posts: BlogPost[]): void {
if (posts.length === 0) {
container.innerHTML = '<p class="no-posts">No posts yet</p>'
return
}
const html = posts.map(post => {
const rkey = post.uri.split('/').pop()
return `
<li class="post-item">
<a href="/post/${rkey}" class="post-link">
<span class="post-title">${escapeHtml(post.title)}</span>
<span class="post-date">${formatDate(post.createdAt)}</span>
</a>
</li>
`
}).join('')
container.innerHTML = `<ul class="post-list">${html}</ul>`
}
export function mountPostDetail(container: HTMLElement, post: BlogPost, handle: string, collection: string, canEdit: boolean = false, siteUrl?: string, network: string = 'bsky.social'): void {
const rkey = post.uri.split('/').pop() || ''
const jsonUrl = `/at/${handle}/${collection}/${rkey}`
const postUrl = siteUrl ? `${siteUrl}/post/${rkey}` : `${window.location.origin}/post/${rkey}`
const appUrl = getAppUrl(network)
const editBtn = canEdit ? `<button class="edit-btn" id="edit-btn">edit</button>` : ''
container.innerHTML = `
<article class="post-detail">
<header class="post-header">
<h1 class="post-title" id="post-title">${escapeHtml(post.title)}</h1>
<div class="post-meta">
<time class="post-date">${formatDate(post.createdAt)}</time>
<a href="${jsonUrl}" class="json-btn">json</a>
${editBtn}
</div>
</header>
<div class="post-content" id="post-content">${renderMarkdown(post.content)}</div>
</article>
${renderDiscussionLink(postUrl, appUrl)}
<div class="edit-form-container" id="edit-form-container" style="display: none;">
<h3>Edit Post</h3>
<form class="edit-form" id="edit-form">
<input type="text" id="edit-title" class="edit-form-title" value="${escapeHtml(post.title)}" placeholder="Title" required>
<textarea id="edit-content" class="edit-form-body" placeholder="Content" required>${escapeHtml(post.content)}</textarea>
<div class="edit-form-footer">
<button type="button" id="edit-cancel" class="edit-cancel-btn">Cancel</button>
<button type="submit" id="edit-submit" class="edit-submit-btn">Save</button>
</div>
</form>
</div>
`
// Load discussion posts
loadDiscussionPosts(container, postUrl)
if (canEdit) {
const editBtnEl = document.getElementById('edit-btn')
const editFormContainer = document.getElementById('edit-form-container')
const editForm = document.getElementById('edit-form') as HTMLFormElement
const editCancel = document.getElementById('edit-cancel')
const postArticle = container.querySelector('.post-detail') as HTMLElement
editBtnEl?.addEventListener('click', () => {
postArticle.style.display = 'none'
editFormContainer!.style.display = 'block'
})
editCancel?.addEventListener('click', () => {
postArticle.style.display = 'block'
editFormContainer!.style.display = 'none'
})
editForm?.addEventListener('submit', async (e) => {
e.preventDefault()
const title = (document.getElementById('edit-title') as HTMLInputElement).value
const content = (document.getElementById('edit-content') as HTMLTextAreaElement).value
const submitBtn = document.getElementById('edit-submit') as HTMLButtonElement
try {
submitBtn.disabled = true
submitBtn.textContent = 'Saving...'
await putRecord(collection, rkey, {
title,
content,
createdAt: post.createdAt,
})
window.location.reload()
} catch (err) {
alert('Save failed: ' + err)
submitBtn.disabled = false
submitBtn.textContent = 'Save'
}
})
}
}