flotilla/src/app/components/ContentLinkBlockImage.svelte

79 lines
2.1 KiB
Svelte
Raw Normal View History

2025-04-28 22:46:48 +00:00
<script lang="ts">
2025-06-03 17:41:38 +00:00
import {onMount, onDestroy} from "svelte"
2025-06-05 20:37:50 +00:00
import {displayUrl} from "@welshman/lib"
import {
getTags,
getBlob,
decryptFile,
getTagValue,
tagsFromIMeta,
makeBlossomAuthEvent,
} from "@welshman/util"
import {signer} from "@welshman/app"
2025-09-10 22:26:39 +00:00
import LinkRound from "@assets/icons/link-round.svg?dataurl"
2025-06-05 20:37:50 +00:00
import Icon from "@lib/components/Icon.svelte"
2025-04-28 22:46:48 +00:00
const {value, event, ...props} = $props()
2025-04-28 22:46:48 +00:00
const url = value.url.toString()
2025-06-03 17:41:38 +00:00
const meta =
getTags("imeta", event.tags)
.map(tagsFromIMeta)
2025-06-03 23:46:30 +00:00
.find(meta => getTagValue("url", meta) === url) || event.tags
2025-06-03 17:41:38 +00:00
const hash = getTagValue("x", meta)
2025-06-03 17:41:38 +00:00
const key = getTagValue("decryption-key", meta)
const nonce = getTagValue("decryption-nonce", meta)
2025-06-06 23:38:11 +00:00
const algorithm = getTagValue("encryption-algorithm", meta)
2025-06-03 17:41:38 +00:00
const onError = async () => {
// If the image failed to load, try authenticating
if (hash && $signer) {
const server = new URL(url).origin
const template = makeBlossomAuthEvent({action: "get", server, hashes: [hash]})
const authEvent = await $signer.sign(template)
const res = await getBlob(server, hash, {authEvent})
if (res.status === 200) {
src = URL.createObjectURL(await res.blob())
} else {
hasError = true
}
} else {
hasError = true
}
2025-06-05 20:37:50 +00:00
}
let hasError = $state(false)
let src = $state("")
2025-06-03 17:41:38 +00:00
onMount(async () => {
// If we have an encryption algorithm, fetch and decrypt
2025-06-06 23:38:11 +00:00
if (algorithm === "aes-gcm" && key && nonce) {
2025-06-03 17:41:38 +00:00
const response = await fetch(url)
if (response.ok) {
const ciphertext = new Uint8Array(await response.arrayBuffer())
2025-06-06 23:38:11 +00:00
const decryptedData = await decryptFile({ciphertext, key, nonce, algorithm})
2025-06-03 17:41:38 +00:00
2025-10-13 20:35:02 +00:00
src = URL.createObjectURL(new Blob([new Uint8Array(decryptedData)]))
2025-06-03 17:41:38 +00:00
}
} else {
src = url
2025-06-03 17:41:38 +00:00
}
})
onDestroy(() => {
URL.revokeObjectURL(src)
})
2025-04-28 22:46:48 +00:00
</script>
2025-06-05 20:37:50 +00:00
{#if hasError}
<a href={url} class="link-content whitespace-nowrap">
2025-09-10 22:26:39 +00:00
<Icon icon={LinkRound} size={3} class="inline-block" />
2025-06-05 20:37:50 +00:00
{displayUrl(url)}
</a>
{:else if src}
2025-06-05 20:37:50 +00:00
<img alt="" {src} onerror={onError} {...props} />
{/if}