tried something different with a custom component
All checks were successful
/ weblog.lol (push) Successful in 12s

This commit is contained in:
Gordon Pedersen 2024-05-11 16:41:57 +10:00
parent f0a0b5de75
commit 7615026a1f
3 changed files with 185 additions and 92 deletions

View file

@ -14,6 +14,7 @@
<img class="avatar" src="https://cdn.some.pics/deathau/663da4619fdb6.svg" alt="My avatar — A pixelated self-portrait in the style of the 'bitizens' in the mobile game Tiny Tower" /> <img class="avatar" src="https://cdn.some.pics/deathau/663da4619fdb6.svg" alt="My avatar — A pixelated self-portrait in the style of the 'bitizens' in the mobile game Tiny Tower" />
<h1 class="weblog-title"><a href="{base-path}">{weblog-title}</a></h1> <h1 class="weblog-title"><a href="{base-path}">{weblog-title}</a></h1>
{navigation} {navigation}
<fedi-social></fedi-social>
</header> </header>
</div> </div>
@ -26,8 +27,8 @@
<i data-emoji="{emoji}"></i> <i data-emoji="{emoji}"></i>
{body} {body}
<aside class="post-info"> <aside class="post-info">
<a class="status_url" href="{status_url}"><i class="omg-icon omg-prami"></i> via status.lol</a> <a class="status_url" href="{status_url}" target="_blank"><i class="omg-icon omg-prami"></i> via status.lol</a>
<a class="external_url" href="{external_url}"><i class="omg-icon omg-fediverse"></i> Interact on the Fediverse</a> <a class="external_url" href="{external_url}" target="_blank"><i class="omg-icon omg-fediverse"></i> Interact on the Fediverse</a>
</aside> </aside>
<aside class="post-tags"> <aside class="post-tags">
{tags} {tags}

View file

@ -6,102 +6,142 @@ Location: /fediverse.js
/**/ /**/
const SUBSCRIBE_LINK_REL = 'http://ostatus.org/schema/1.0/subscribe' const SUBSCRIBE_LINK_REL = 'http://ostatus.org/schema/1.0/subscribe'
async function getSubscribeTemplate() { const AVATAR_LINK_REL = 'http://webfinger.net/rel/avatar'
const input = prompt("Please enter your fediverse / mastodon handle (e.g. '@user@domain.social')", "@")
let handle = input.trim().replace(/^@/,'')
const split = handle.split('@')
if(split.length == 2) {
const resource = `acct:${handle}`
const domain = split[1]
// look up remote user via webfinger class FediSocial extends HTMLElement {
const url = `https://${domain}/.well-known/webfinger?resource=${resource}` constructor() {
return fetch(url, {headers: { // Always call super first in constructor
'Content-Type': 'application/json' super();
}}).then(async result => {
const json = await result.json()
const subscribe = json.links.find(link => link.rel && link.rel == SUBSCRIBE_LINK_REL)
let template = subscribe.template
return template
})
.catch(e => {
console.error(e)
alert(`Sorry, we couldn't find an uri for ${input}.\n\nTry searching for "${uri}" on ${domain} (or in your fediverse client of choice)`)
return null
})
} }
else {
alert('Please enter your fediverse address in @user@domain.social format')
return null
}
return null
}
function linkToUrl(uri) { connectedCallback() {
// window.open(uri, '_blank').focus() const data = JSON.parse(localStorage.getItem('fedi-social') || '{}')
const link = document.createElement('a')
link.href = uri
link.target = "_blank"
link.click()
}
function noDataCick(href) { let img;
return async (e) => { if(data.avatar) {
e.preventDefault() img = document.createElement('img')
let subscribe_template = await getSubscribeTemplate() img.src = data.avatar || "https://upload.wikimedia.org/wikipedia/commons/9/93/Fediverse_logo_proposal.svg"
if(subscribe_template) {
localStorage.setItem('subscribe_template', subscribe_template)
const url = subscribe_template.replace("{uri}", href)
linkToUrl(url)
addSocialButtons(this, url)
} }
else { else {
linkToUrl(href) img = document.createElement('i')
img.classList.add('omg-icon', 'omg-fediverse')
} }
const summary = document.createElement('summary')
summary.appendChild(img)
const details = document.createElement('details')
details.appendChild(summary)
// popup
const div = document.createElement('div')
div.classList.add('card', 'popover')
if(data.id) {
//logout
const p = document.createElement('p')
p.innerHTML = `Hey again, ${data.name} 👋`
div.appendChild(p)
const button = document.createElement('button')
button.innerText = "Forget me"
button.addEventListener('click', ev => { localStorage.clear(); window.location.reload() })
div.appendChild(button)
}
else {
const p = document.createElement('p')
p.innerHTML = "Hey 👋, <br />please enter your fediverse / mastodon handle below. "
const form = document.createElement('form')
form.addEventListener('submit', this.login)
const input = document.createElement('input')
input.type = 'text'
input.name = 'handle'
input.placeholder = '@user@domain.social'
form.appendChild(input)
const button = document.createElement('input')
button.type = 'submit'
button.value = 'Submit'
form.appendChild(button)
div.appendChild(p)
div.appendChild(form)
}
details.appendChild(div)
this.appendChild(details)
}
login(ev) {
ev.preventDefault()
const input = this.elements['handle'].value
let handle = input.trim().replace(/^@/,'')
const split = handle.split('@')
if(split.length == 2) {
const resource = `acct:${handle}`
const domain = split[1]
// look up remote user via webfinger
const url = `https://${domain}/.well-known/webfinger?resource=${resource}`
return fetch(url, {headers: {
'Accept': 'application/json'
}}).then(async result => {
const json = await result.json()
console.log(json)
const template = json.links.find(link => link.rel && link.rel == SUBSCRIBE_LINK_REL)?.template
const avatar = json.links.find(link => link.rel && link.rel == AVATAR_LINK_REL)?.href
const id = json.links.find(link => link.rel && link.rel == "self" && link.type == "application/activity+json")?.href
const data = { id, template, avatar, name: input }
localStorage.setItem('fedi-social', JSON.stringify(data))
window.location.reload()
})
.catch(e => {
console.error(e)
this.parentElement.querySelector('p').innerHTML = `Sorry, we couldn't find details for ${input}.\n\nTo interact with posts, try searching for their url on ${domain} (or in your fediverse client of choice)`
return null
})
}
else {
this.parentElement.querySelector('p').innerHTML = `Please enter your fediverse address in @user@domain.social format`
}
}
clicked(ev) {
ev.preventDefault()
console.log("Ow! You clicked me!")
} }
} }
function addSocialButtons(el, href) { customElements.define('fedi-social', FediSocial)
// TODO: fetch the post from the fediverse to add in comment, like and retweet count
// the following fetches the activitypub object but has no reference to the likes, etc
// if I had the local id, I could use the mastodon API to get it, but friendica uses the diaspora:guid in the link
// const url = new URL(href).searchParams.get("uri")
// fetch(url, {
// headers: {
// 'Accept': 'application/json'
// }
// }).then(async result => {
// const json = await result.text()
// console.log(json)
// })
// .catch(e => {
// console.error(e)
// })
const innerHTML = `
<a href='${href}' target="_blank"><i class="fa fa-comment"></i></a>
<a href='${href}' target="_blank"><i class="fa fa-star"></i></a>
<a href='${href}' target="_blank"><i class="fa fa-retweet"></i></a>
`
const span = document.createElement('span')
span.classList.add('fediverse')
span.innerHTML = innerHTML
el.insertAdjacentElement('afterend', span)
el.remove()
}
function fediverse() { function fediverse() {
let subscribe_template = localStorage.getItem('subscribe_template'); const data = JSON.parse(localStorage.getItem('fedi-social') || '{}')
document.querySelectorAll("a.external_url:not([href='{external_url}'])").forEach(el => {
if(data.template){
const innerHTML = `
<a href='${href}' target="_blank"><i class="fa fa-comment"></i></a>
<a href='${href}' target="_blank"><i class="fa fa-star"></i></a>
<a href='${href}' target="_blank"><i class="fa fa-retweet"></i></a>
`
document.querySelectorAll("a.external_url:not([href='{external_url}'])").forEach(el => { const span = document.createElement('span')
if(subscribe_template) { span.classList.add('fediverse')
console.log(subscribe_template.replace("{uri}", el.href)) span.innerHTML = innerHTML
addSocialButtons(el, subscribe_template.replace("{uri}", el.href))
} el.insertAdjacentElement('afterend', span)
else { el.remove()
el.addEventListener('click', noDataCick(el.href)) }
} else {
}) el.addEventListener('click', ev =>{
ev.preventDefault()
window.scrollTo({ top: 0, behavior: 'smooth' })
document.querySelector('fedi-social summary').click()
})
}
})
} }

View file

@ -59,7 +59,8 @@ body {
} }
article, article,
main.page { main.page,
.card {
display: block; display: block;
max-width: 42em; max-width: 42em;
border-radius: 1em; border-radius: 1em;
@ -336,3 +337,54 @@ th {
text-align: left; text-align: left;
border: 1px solid var(--accent); border: 1px solid var(--accent);
} }
fedi-social {
margin-left:auto;
position: relative;
}
fedi-social summary {
width: 48px;
height: 48px;
border-radius: 50%;
display: inline-block;
background-color: var(--accent);
text-align: center;
vertical-align: middle;
cursor: pointer;
}
fedi-social img {
width: 48px;
height: 48px;
border-radius: 50%;
}
fedi-social summary + * {
position:absolute;
}
fedi-social .popover {
max-width: 300px;
min-height: 100px;
font-size: 85%;
position: absolute;
right: 0;
top: 0;
z-index: 100;
border-radius: 1em 0 1em 1em;
}
fedi-social .popover p {
line-height: 1.2em;
}
fedi-social a {
background: none;
}
fedi-social i::before {
font-size: 32px;
line-height: 48px;
}