Support auth-protected images
This commit is contained in:
@@ -141,7 +141,7 @@
|
|||||||
<ContentToken value={parsed.value} />
|
<ContentToken value={parsed.value} />
|
||||||
{:else if isLink(parsed)}
|
{:else if isLink(parsed)}
|
||||||
{#if isBlock(i)}
|
{#if isBlock(i)}
|
||||||
<ContentLinkBlock value={parsed.value} />
|
<ContentLinkBlock value={parsed.value} {event} />
|
||||||
{:else}
|
{:else}
|
||||||
<ContentLinkInline value={parsed.value} />
|
<ContentLinkInline value={parsed.value} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
import {preventDefault, stopPropagation} from "@lib/html"
|
import {preventDefault, stopPropagation} from "@lib/html"
|
||||||
import Link from "@lib/components/Link.svelte"
|
import Link from "@lib/components/Link.svelte"
|
||||||
import ContentLinkDetail from "@app/components/ContentLinkDetail.svelte"
|
import ContentLinkDetail from "@app/components/ContentLinkDetail.svelte"
|
||||||
|
import ContentLinkBlockImage from "@app/components/ContentLinkBlockImage.svelte"
|
||||||
import {pushModal} from "@app/modal"
|
import {pushModal} from "@app/modal"
|
||||||
|
|
||||||
const {value} = $props()
|
const {value, event} = $props()
|
||||||
|
|
||||||
let hideImage = $state(false)
|
let hideImage = $state(false)
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@
|
|||||||
</video>
|
</video>
|
||||||
{:else if url.match(/\.(jpe?g|png|gif|webp)$/)}
|
{:else if url.match(/\.(jpe?g|png|gif|webp)$/)}
|
||||||
<button type="button" onclick={stopPropagation(preventDefault(expand))}>
|
<button type="button" onclick={stopPropagation(preventDefault(expand))}>
|
||||||
<img alt="Link preview" src={imgproxy(url)} class="m-auto max-h-96 rounded-box" />
|
<ContentLinkBlockImage {event} {value} />
|
||||||
</button>
|
</button>
|
||||||
{:else}
|
{:else}
|
||||||
{#await loadPreview()}
|
{#await loadPreview()}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {onDestroy} from "svelte"
|
||||||
|
import {now} from "@welshman/lib"
|
||||||
|
import {BLOSSOM_AUTH, makeEvent, getTags, getTagValue, tagsFromIMeta} from "@welshman/util"
|
||||||
|
import {signer} from "@welshman/app"
|
||||||
|
import {imgproxy} from "@app/state"
|
||||||
|
|
||||||
|
const {value, event} = $props()
|
||||||
|
|
||||||
|
const url = value.url.toString()
|
||||||
|
|
||||||
|
// If we fail to fetch the image, try authenticating if we have a blossom hash
|
||||||
|
const onerror = async () => {
|
||||||
|
const meta = getTags("imeta", event.tags)
|
||||||
|
.map(tagsFromIMeta)
|
||||||
|
.find(meta => getTagValue("url", meta) === url)
|
||||||
|
const hash = meta ? getTagValue("x", meta) : undefined
|
||||||
|
|
||||||
|
if (hash && $signer) {
|
||||||
|
const event = await signer.get().sign(
|
||||||
|
makeEvent(BLOSSOM_AUTH, {
|
||||||
|
tags: [
|
||||||
|
["t", "get"],
|
||||||
|
["x", hash],
|
||||||
|
["expiration", String(now() + 30)],
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
const res = await fetch(url, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Nostr ${btoa(JSON.stringify(event))}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
src = URL.createObjectURL(await res.blob())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let src = $state(imgproxy(url))
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
URL.revokeObjectURL(src)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<img alt="" {src} {onerror} class="m-auto max-h-96 rounded-box" />
|
||||||
+4
-1
@@ -41,6 +41,7 @@ import {
|
|||||||
loadMutes,
|
loadMutes,
|
||||||
loadFollows,
|
loadFollows,
|
||||||
loadProfile,
|
loadProfile,
|
||||||
|
loadRelaySelections,
|
||||||
loadInboxRelaySelections,
|
loadInboxRelaySelections,
|
||||||
} from "@welshman/app"
|
} from "@welshman/app"
|
||||||
import {createScroller} from "@lib/html"
|
import {createScroller} from "@lib/html"
|
||||||
@@ -384,7 +385,9 @@ export const listenForNotifications = () => {
|
|||||||
return () => controller.abort()
|
return () => controller.abort()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadUserData = (pubkey: string, relays: string[] = []) => {
|
export const loadUserData = async (pubkey: string, relays: string[] = []) => {
|
||||||
|
await Promise.race([sleep(3000), loadRelaySelections(pubkey, relays)])
|
||||||
|
|
||||||
const promise = Promise.race([
|
const promise = Promise.race([
|
||||||
sleep(3000),
|
sleep(3000),
|
||||||
Promise.all([
|
Promise.all([
|
||||||
|
|||||||
+1
-1
@@ -115,7 +115,7 @@ export const IMGPROXY_URL = "https://imgproxy.coracle.social"
|
|||||||
export const REACTION_KINDS = [REACTION, ZAP_RESPONSE]
|
export const REACTION_KINDS = [REACTION, ZAP_RESPONSE]
|
||||||
|
|
||||||
export const NIP46_PERMS =
|
export const NIP46_PERMS =
|
||||||
"nip04_encrypt,nip04_decrypt,nip44_encrypt,nip44_decrypt," +
|
"nip44_encrypt,nip44_decrypt," +
|
||||||
[CLIENT_AUTH, AUTH_JOIN, MESSAGE, THREAD, COMMENT, GROUPS, WRAP, REACTION]
|
[CLIENT_AUTH, AUTH_JOIN, MESSAGE, THREAD, COMMENT, GROUPS, WRAP, REACTION]
|
||||||
.map(k => `sign_event:${k}`)
|
.map(k => `sign_event:${k}`)
|
||||||
.join(",")
|
.join(",")
|
||||||
|
|||||||
@@ -18,16 +18,17 @@
|
|||||||
|
|
||||||
const onsubmit = preventDefault(async () => {
|
const onsubmit = preventDefault(async () => {
|
||||||
const json = JSON.stringify($state.snapshot(settings))
|
const json = JSON.stringify($state.snapshot(settings))
|
||||||
const content = await $signer!.nip04.encrypt($pubkey!, json)
|
const content = await $signer!.nip44.encrypt($pubkey!, json)
|
||||||
|
const relays = Router.get().FromUser().getUrls()
|
||||||
|
|
||||||
publishThunk({
|
publishThunk({
|
||||||
event: createEvent(SETTINGS, {content}),
|
event: createEvent(SETTINGS, {content}),
|
||||||
relays: Router.get().FromUser().getUrls(),
|
relays,
|
||||||
})
|
})
|
||||||
|
|
||||||
publishThunk({
|
publishThunk({
|
||||||
event: createEvent(MUTES, {tags: mutedPubkeys.map(tagPubkey)}),
|
event: createEvent(MUTES, {tags: mutedPubkeys.map(tagPubkey)}),
|
||||||
relays: Router.get().FromUser().getUrls(),
|
relays,
|
||||||
})
|
})
|
||||||
|
|
||||||
pushToast({message: "Your settings have been saved!"})
|
pushToast({message: "Your settings have been saved!"})
|
||||||
|
|||||||
Reference in New Issue
Block a user