/**
* Timeline rendering
*/
function renderPost (item) {
const el = document.createElement('div')
el.className = 'post' + (item.type === 'boost' ? ' post-type-boost' : '') + (item.type === 'reply' ? ' post-type-reply' : '')
const platformLabel = {
mastodon: 'Mastodon',
atproto: 'Bluesky',
instagram: 'Instagram',
local: 'Local'
}
const time = new Date(item.timestamp)
const timeStr = time.toLocaleDateString() + ' ' + time.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
let boostInfo = ''
if (item.type === 'boost' && item.metadata?.boostBy) {
boostInfo = `<div style="font-size:12px;color:var(--text-muted);margin-bottom:4px">${item.metadata.boostBy} reposted</div>`
}
let mediaHtml = ''
if (item.mediaUrl) {
if (item.mediaType === 'image' || item.mediaType === 'gifv') {
mediaHtml = `<div class="post-media"><img src="${escapeHtml(item.mediaUrl)}" loading="lazy" alt=""></div>`
} else if (item.mediaType === 'video') {
mediaHtml = `<div class="post-media"><a href="${escapeHtml(item.mediaUrl)}" target="_blank">[video]</a></div>`
}
}
let metaHtml = ''
if (item.metadata) {
const parts = []
if (item.metadata.likeCount) parts.push(item.metadata.likeCount + ' likes')
if (item.metadata.repostCount) parts.push(item.metadata.repostCount + ' reposts')
if (item.metadata.replyCount) parts.push(item.metadata.replyCount + ' replies')
if (parts.length > 0) metaHtml = `<div class="post-meta">${parts.join(' · ')}</div>`
}
let syndicatedHtml = ''
if (item.syndicated) {
const platforms = Object.values(item.syndicated).map(s => s.platform).join(', ')
syndicatedHtml = `<div class="post-syndicated">Published to: ${platforms}</div>`
}
let urlHtml = ''
if (item.url) {
urlHtml = `<div style="margin-top:4px"><a href="${escapeHtml(item.url)}" target="_blank" style="color:var(--accent);font-size:12px">View on ${platformLabel[item.source] || item.source}</a></div>`
}
const avatar = item.metadata?.avatar || ''
const displayName = item.metadata?.displayName || ''
const avatarHtml = avatar ? `<img class="post-avatar" src="${escapeHtml(avatar)}" alt="">` : `<div class="post-avatar post-avatar-placeholder"></div>`
el.innerHTML = `
${boostInfo}
<div class="post-header">
${avatarHtml}
<div class="post-author-info">
${displayName ? `<span class="post-displayname">${escapeHtml(displayName)}</span>` : ''}
<span class="post-author">${escapeHtml(item.author)}</span>
</div>
<span class="post-platform ${item.source}">${platformLabel[item.source] || item.source}</span>
<span class="post-time">${timeStr}</span>
</div>
<div class="post-text">${escapeHtml(item.text)}</div>
${mediaHtml}
${metaHtml}
${syndicatedHtml}
${urlHtml}
`
return el
}
function renderNotification (notif) {
const el = document.createElement('div')
el.className = 'notification'
const platformLabel = { mastodon: 'Mastodon', atproto: 'Bluesky' }
const time = new Date(notif.timestamp)
const timeStr = time.toLocaleDateString() + ' ' + time.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
el.innerHTML = `
<div class="post-header">
<span class="notif-type">${escapeHtml(notif.type)}</span>
<span class="post-platform ${notif.platform}">${platformLabel[notif.platform] || notif.platform}</span>
<span class="post-time">${timeStr}</span>
</div>
<div><strong>${escapeHtml(notif.author)}</strong></div>
${notif.text ? `<div class="post-text">${escapeHtml(notif.text)}</div>` : ''}
`
return el
}
function renderAccountCard (account) {
const el = document.createElement('div')
el.className = 'account-card'
el.innerHTML = `
<div class="account-info">
<span class="post-platform ${account.platform}">${account.platform}</span>
<span>${escapeHtml(account.handle || account.instance || account.id)}</span>
</div>
<button class="btn btn-small btn-danger" onclick="disconnectAccount('${escapeHtml(account.id)}')">Disconnect</button>
`
return el
}
function escapeHtml (str) {
if (!str) return ''
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')
}