118 lines
4.2 KiB
TypeScript
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'
|
|
}
|
|
})
|
|
}
|
|
}
|