diff --git a/actor-mini-profile.css b/actor-mini-profile.css
index 5579e0c..dec788c 100644
--- a/actor-mini-profile.css
+++ b/actor-mini-profile.css
@@ -11,14 +11,6 @@
font: inherit;
}
-.profile-mini-icon {
- width: 28px;
- height: 28px;
- border-radius: 50%;
- background-color: #000000;
- margin-right: 6px;
-}
-
.profile-mini-name {
color: var(--rdp-text-color);
}
diff --git a/actor-mini-profile.js b/actor-mini-profile.js
index 319319a..e781a9f 100644
--- a/actor-mini-profile.js
+++ b/actor-mini-profile.js
@@ -50,6 +50,7 @@ class ActorMiniProfile extends HTMLElement {
// Actor icon
const p2pImage = document.createElement('p2p-image')
p2pImage.className = 'profile-mini-icon'
+ p2pImage.setAttribute('src', iconUrl)
p2pImage.alt = actorInfo.name ? actorInfo.name : 'Actor icon'
clickableContainer.appendChild(p2pImage)
diff --git a/actor-profile.css b/actor-profile.css
index 5d9e668..d9f1bb3 100644
--- a/actor-profile.css
+++ b/actor-profile.css
@@ -13,15 +13,6 @@
margin-bottom: 16px;
}
-.profile-icon {
- width: 50px;
- height: 50px;
- border-radius: 50%;
- background-color: #000000;
- margin-right: 8px;
- margin-bottom: 8px;
-}
-
.profile-details {
display: flex;
flex-direction: column;
diff --git a/actor-profile.js b/actor-profile.js
index 066c31e..c204c76 100644
--- a/actor-profile.js
+++ b/actor-profile.js
@@ -70,8 +70,8 @@ class ActorProfile extends HTMLElement {
}
const p2pImage = document.createElement('p2p-image')
- p2pImage.setAttribute('src', iconUrl)
p2pImage.classList.add('profile-icon')
+ p2pImage.setAttribute('src', iconUrl)
p2pImage.alt = actorInfo.name ? actorInfo.name : 'Actor icon'
actorContainer.appendChild(p2pImage) // Append to the actor container
diff --git a/db.js b/db.js
index daa267b..0530b0e 100644
--- a/db.js
+++ b/db.js
@@ -42,14 +42,32 @@ export function isP2P (url) {
return url.startsWith(HYPER_PREFIX) || url.startsWith(IPNS_PREFIX)
}
+// Global cache to store protocol reachability
+const protocolSupportMap = new Map()
+
export async function supportsP2P (url) {
- try {
- const response = await fetch(url)
- return response.ok
- } catch (error) {
- console.log('P2P URL loading failed:', error)
- return false
+ const urlObject = new URL(url)
+ const protocol = urlObject.protocol
+
+ if (protocolSupportMap.has(protocol)) {
+ return protocolSupportMap.get(protocol)
}
+
+ // Set a promise to avoid multiple simultaneous checks
+ const supportCheckPromise = fetch(url)
+ .then(response => {
+ const supported = response.ok
+ protocolSupportMap.set(protocol, supported)
+ return supported
+ })
+ .catch(error => {
+ console.error(`Error checking protocol support for ${protocol}:`, error)
+ protocolSupportMap.set(protocol, false)
+ return false
+ })
+
+ protocolSupportMap.set(protocol, supportCheckPromise)
+ return supportCheckPromise
}
export function resolveP2PUrl (url) {
diff --git a/example/post.html b/example/post.html
index 5b99b51..4b3448c 100644
--- a/example/post.html
+++ b/example/post.html
@@ -10,7 +10,8 @@
-
+
+
-
diff --git a/outbox.js b/outbox.js
index 0989b90..b47f13f 100644
--- a/outbox.js
+++ b/outbox.js
@@ -1,4 +1,5 @@
import { db } from './dbInstance.js'
+import './post.js'
class DistributedOutbox extends HTMLElement {
skip = 0
diff --git a/p2p-media.css b/p2p-media.css
new file mode 100644
index 0000000..31b506b
--- /dev/null
+++ b/p2p-media.css
@@ -0,0 +1,31 @@
+p2p-image,
+p2p-video {
+ max-width: 100%;
+}
+
+.profile-icon {
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
+ background-color: #000000;
+ margin-right: 8px;
+ margin-bottom: 8px;
+ }
+
+
+.profile-mini-icon {
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ background-color: #000000;
+ margin-right: 6px;
+ }
+
+ .actor-icon {
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
+ background-color: #000000;
+ margin-right: 8px;
+ cursor: pointer;
+ }
\ No newline at end of file
diff --git a/p2p-media.js b/p2p-media.js
index d6469f8..7933c3d 100644
--- a/p2p-media.js
+++ b/p2p-media.js
@@ -1,28 +1,43 @@
-import { supportsP2P, resolveP2PUrl } from './db.js'
+import { supportsP2P, resolveP2PUrl, isP2P } from './db.js'
class P2PImage extends HTMLElement {
constructor () {
super()
+ this.attachShadow({ mode: 'open' })
+ // Create an img element
this.img = document.createElement('img')
- this.attachShadow({ mode: 'open' })
- this.shadowRoot.appendChild(this.img)
+
+ // Load the CSS file
+ const style = document.createElement('style')
+ fetch('./p2p-media.css').then(response => response.text()).then(css => {
+ style.textContent = css
+ this.shadowRoot.append(style, this.img)
+ })
+
this.img.addEventListener('error', () => this.handleError())
}
static get observedAttributes () {
- return ['src']
+ return ['src', 'class']
}
connectedCallback () {
if (this.hasAttribute('src')) {
this.loadImage(this.getAttribute('src'))
}
+ this.syncClass()
+ this.addEventListener('load-error', e => {
+ console.log(`Handled fallback for src: ${e.detail.fallbackSrc}`)
+ })
}
attributeChangedCallback (name, oldValue, newValue) {
if (name === 'src' && newValue !== oldValue) {
this.loadImage(newValue)
+ this.img.src = newValue // Ensure the inner image src is updated immediately
+ } else if (name === 'class' && newValue !== oldValue) {
+ this.syncClass()
}
}
@@ -39,10 +54,20 @@ class P2PImage extends HTMLElement {
}
}
- async handleError () {
- const fallbackSrc = resolveP2PUrl(this.getAttribute('src'))
- console.log(`Failed to load image. Resolving to gateway URL: ${fallbackSrc}`)
- this.img.src = fallbackSrc
+ syncClass () {
+ const classes = this.className.split(' ')
+ this.img.className = classes.join(' ')
+ }
+
+ handleError () {
+ const src = this.getAttribute('src')
+ if (isP2P(src)) {
+ const fallbackSrc = resolveP2PUrl(src)
+ console.log(`Failed to load, resolving to gateway URL: ${fallbackSrc}`)
+ this.img.src = fallbackSrc
+ this.setAttribute('src', fallbackSrc) // Update the attribute to the fallback source
+ this.dispatchEvent(new CustomEvent('load-error', { detail: { fallbackSrc } }))
+ }
}
}
@@ -51,25 +76,40 @@ customElements.define('p2p-image', P2PImage)
class P2PVideo extends HTMLElement {
constructor () {
super()
+ this.attachShadow({ mode: 'open' })
+
+ // Create a video element
this.video = document.createElement('video')
this.video.controls = true
- this.attachShadow({ mode: 'open' })
- this.shadowRoot.appendChild(this.video)
+
+ // Load the CSS file
+ const style = document.createElement('style')
+ fetch('./p2p-media.css').then(response => response.text()).then(css => {
+ style.textContent = css
+ this.shadowRoot.append(style, this.video)
+ })
}
static get observedAttributes () {
- return ['src']
+ return ['src', 'class']
}
connectedCallback () {
if (this.hasAttribute('src')) {
this.loadVideo(this.getAttribute('src'))
}
+ this.syncClass()
+ this.addEventListener('load-error', e => {
+ console.log(`Handled fallback for src: ${e.detail.fallbackSrc}`)
+ })
}
attributeChangedCallback (name, oldValue, newValue) {
if (name === 'src' && newValue !== oldValue) {
this.loadVideo(newValue)
+ this.video.src = newValue // Ensure the inner video src is updated immediately
+ } else if (name === 'class' && newValue !== oldValue) {
+ this.syncClass()
}
}
@@ -92,10 +132,20 @@ class P2PVideo extends HTMLElement {
this.video.appendChild(source)
}
+ syncClass () {
+ const classes = this.className.split(' ')
+ this.video.className = classes.join(' ')
+ }
+
handleError (source) {
- const fallbackSrc = resolveP2PUrl(source.src)
- console.log(`Failed to load video source. Resolving to gateway URL: ${fallbackSrc}`)
- source.src = fallbackSrc
+ const src = source.src
+ if (isP2P(src)) {
+ const fallbackSrc = resolveP2PUrl(src)
+ console.log(`Failed to load video source. Resolving to gateway URL: ${fallbackSrc}`)
+ source.src = fallbackSrc
+ this.setAttribute('src', fallbackSrc) // Update the attribute to the fallback source
+ this.dispatchEvent(new CustomEvent('load-error', { detail: { fallbackSrc } }))
+ }
}
}
diff --git a/post.css b/post.css
index f788527..339098a 100644
--- a/post.css
+++ b/post.css
@@ -32,15 +32,6 @@
align-items: center;
}
-.actor-icon {
- width: 50px;
- height: 50px;
- border-radius: 50%;
- background-color: #000000;
- margin-right: 8px;
- cursor: pointer;
-}
-
.actor-details {
display: flex;
flex-direction: column;
diff --git a/post.html b/post.html
index d3f2451..fe4fa5d 100644
--- a/post.html
+++ b/post.html
@@ -33,7 +33,6 @@
-
diff --git a/post.js b/post.js
index 60aaf9f..3118e5d 100644
--- a/post.js
+++ b/post.js
@@ -1,7 +1,7 @@
/* global customElements, HTMLElement */
import DOMPurify from './dependencies/dompurify/purify.js'
import { db } from './dbInstance.js'
-import { resolveP2PUrl, isP2P } from './db.js'
+import './p2p-media.js'
function formatDate (dateString) {
const options = { year: 'numeric', month: 'short', day: 'numeric' }
@@ -40,42 +40,41 @@ function timeSince (dateString) {
return Math.floor(seconds) + 's'
}
-function insertImagesAndVideos (content) {
+async function insertImagesAndVideos (content) {
const parser = new DOMParser()
const contentDOM = parser.parseFromString(content, 'text/html')
- // Replace all
tags with tags
- contentDOM.querySelectorAll('img').forEach(img => {
+ const imgPromises = Array.from(contentDOM.querySelectorAll('img')).map(async (img) => {
const originalSrc = img.getAttribute('src')
- console.log(`Original img src: ${originalSrc}`)
const p2pImg = document.createElement('p2p-image')
p2pImg.setAttribute('src', originalSrc)
+
+ p2pImg.addEventListener('load-error', (event) => {
+ console.log(`Error loading image at ${originalSrc}, fallback to ${event.detail.fallbackSrc}`)
+ p2pImg.setAttribute('src', event.detail.fallbackSrc)
+ console.log('Updated p2p-image src after fallback:', p2pImg.getAttribute('src'))
+ })
+
img.parentNode.replaceChild(p2pImg, img)
- console.log(`Replaced img with p2p-image having src: ${p2pImg.getAttribute('src')}`)
+ console.log('Replaced img with p2p-image having initial src:', p2pImg.getAttribute('src'))
})
- // Replace all