티스토리 수익 글 보기
import { db } from ‘./dbInstance.js’
import { applyDefaults } from ‘./defaults.js’
import ‘./post.js’
class DistributedOutbox extends HTMLElement {
skip = 0
limit = 32
constructor () {
super()
this.renderedItems = new Map() // Tracks rendered items by ID
}
static get observedAttributes () {
return [‘url’]
}
async connectedCallback () {
await applyDefaults()
this.outboxUrl = this.getAttribute(‘url’)
this.loadOutbox(this.outboxUrl)
}
async loadOutbox (outboxUrl) {
this.clearContent()
const items = await this.collectItems(outboxUrl, { skip: this.skip, limit: this.limit + 1 })
items.slice(0, this.limit).forEach(item => this.processItem(item))
// Update skip for next potential load
this.skip += this.limit
// Check if there are more items to load
if (items.length > this.limit) {
this.createLoadMoreButton()
}
}
async loadMore () {
this.removeLoadMoreButton()
const items = await this.collectItems(this.outboxUrl, { skip: this.skip, limit: this.limit + 1 })
items.slice(0, this.limit).forEach(item => this.processItem(item))
this.skip += this.limit
if (items.length > this.limit) {
this.createLoadMoreButton()
}
}
async collectItems (outboxUrl, { skip, limit }) {
const items = []
for await (const item of db.iterateCollection(outboxUrl, { skip, limit })) {
items.push(item)
}
return items
}
processItem (item) {
const itemKey = item.id || item.object
if (!itemKey) {
console.error(‘Item key is undefined, item:’, item)
return
}
if (!this.renderedItems.has(itemKey)) {
this.renderItem(item)
this.renderedItems.set(itemKey, true)
}
}
renderItem (item) {
const activityElement = document.createElement(‘distributed-activity’)
activityElement.type = item.type
activityElement.data = item
this.appendChild(activityElement)
}
createLoadMoreButton () {
this.removeLoadMoreButton()
const loadMoreBtn = document.createElement(‘button’)
loadMoreBtn.textContent = ‘Load More’
loadMoreBtn.className = ‘load-more-btn’
const loadMoreBtnWrapper = document.createElement(‘div’)
loadMoreBtnWrapper.className = ‘load-more-btn-container’
loadMoreBtnWrapper.appendChild(loadMoreBtn)
loadMoreBtn.addEventListener(‘click’, () => this.loadMore())
this.appendChild(loadMoreBtnWrapper)
}
clearContent () {
this.innerHTML = ”
this.renderedItems.clear()
}
removeLoadMoreButton () {
const loadMoreBtnWrapper = this.querySelector(‘.load-more-btn-container’)
if (loadMoreBtnWrapper) {
loadMoreBtnWrapper.remove()
}
}
attributeChangedCallback (name, oldValue, newValue) {
if (name === ‘url’ && newValue !== oldValue) {
this.outboxUrl = newValue
this.loadOutbox(this.outboxUrl)
}
}
}
// Register the new element with the browser
customElements.define(‘distributed-outbox’, DistributedOutbox)
class DistributedActivity extends HTMLElement {
constructor () {
super()
this.activityType = ”
this.activityData = {}
this.activityUrl = null
}
static get observedAttributes () {
return [‘type’, ‘data’, ‘url’]
}
async connectedCallback () {
// Check if the component already has type and data set as properties
if (this.type && this.data) {
this.activityType = this.type
this.activityData = this.data
this.renderActivity()
} else if (this.activityUrl) {
// Load from URL if type and data are not set
await this.loadDataFromUrl(this.activityUrl)
} else {
console.error(‘Activity data is not provided and no URL is specified.’)
}
}
async loadDataFromUrl (activityUrl) {
try {
const activityData = await db.getActivity(activityUrl)
this.type = activityData.type
this.data = activityData
this.connectedCallback()
} catch (error) {
console.error(‘Error loading activity data from URL:’, error)
}
}
async fetchAndDisplayPost () {
let postUrl
// Determine the source of the post (direct activity or URL pointing to the activity)
const isDirectPost =
typeof this.activityData.object === ‘string’ ||
this.activityData.object instanceof String
if (isDirectPost) {
postUrl = this.activityData.object
} else if (this.activityData.object && this.activityData.object.id) {
postUrl = this.activityData.object.id
} else {
postUrl = this.activityData.object
}
// Create and append the distributed-post component without clearing previous content
const distributedPostElement = document.createElement(‘distributed-post’)
distributedPostElement.setAttribute(‘url’, postUrl)
this.appendChild(distributedPostElement)
}
displayUnimplemented () {
const message = `Activity type ${this.activityType} is not implemented yet.`
const messageElement = document.createElement(‘p’)
messageElement.classList.add(‘other-activity’)
messageElement.textContent = message
this.appendChild(messageElement)
}
renderActivity () {
// Clear existing content
this.innerHTML = ”
switch (this.activityType) {
case ‘Create’:
this.fetchAndDisplayPost()
break
case ‘Update’:
this.fetchAndDisplayPost()
break
case ‘Announce’:
this.displayRepostedActivity()
break
case ‘Follow’:
this.displayFollowActivity()
break
case ‘Like’:
this.displayLikeActivity()
break
default:
this.displayUnimplemented()
break
}
}
displayRepostedActivity () {
const actorUrl = this.activityData.actor
db.getActor(actorUrl).then(actorData => {
const actorDisplayName = actorData.preferredUsername || actorData.name || actorUrl.split(‘/’).pop().split(‘@’).pop() // Fallback to URL parsing if name is unavailable
const repostLabel = document.createElement(‘p’)
repostLabel.textContent = `Reposted by ${actorDisplayName} ⇄`
repostLabel.className = ‘repost-label’
this.appendChild(repostLabel)
this.fetchAndDisplayPost()
}).catch(error => {
console.error(‘Error loading actor data:’, error)
this.fetchAndDisplayPost() // Continue to display the post even if actor loading fails
})
}
displayFollowActivity () {
const from = this.activityData.actor
const to = this.activityData.object
const message = `New follow request from ${from} to ${to}`
const messageElement = document.createElement(‘p’)
messageElement.classList.add(‘other-activity’)
messageElement.textContent = message
this.appendChild(messageElement)
}
displayLikeActivity () {
const message = `New like on ${this.activityData.object}`
const messageElement = document.createElement(‘p’)
messageElement.classList.add(‘other-activity’)
messageElement.textContent = message
this.appendChild(messageElement)
}
attributeChangedCallback (name, oldValue, newValue) {
if (newValue !== oldValue) {
if (name === ‘type’) {
this.activityType = newValue
this.renderActivity()
} else if (name === ‘data’) {
this.activityData = JSON.parse(newValue)
this.renderActivity()
} else if (name === ‘url’) {
this.loadDataFromUrl(newValue)
.then(() => {
this.renderActivity()
})
.catch((error) => {
console.error(‘Error loading activity data from URL:’, error)
})
}
}
}
}
// Register the new element with the browser
customElements.define(‘distributed-activity’, DistributedActivity)