Resolve a link content type per url rather than per event

This commit is contained in:
Coracle-Bot 2026-09-08 21:38:50 +00:00 committed by hodlbod
parent 8e46f5a15b
commit 1da59fb322
4 changed files with 39 additions and 20 deletions

View file

@ -16,7 +16,7 @@
<script lang="ts"> <script lang="ts">
import {call, ellipsize, displayUrl} from "@welshman/lib" import {call, ellipsize, displayUrl} from "@welshman/lib"
import {isRelayUrl, tagSpec, tagValue} from "@welshman/util" import {isRelayUrl} from "@welshman/util"
import {Capacitor} from "@capacitor/core" import {Capacitor} from "@capacitor/core"
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"
@ -26,7 +26,12 @@
import ContentLinkBlockImage from "@app/components/ContentLinkBlockImage.svelte" import ContentLinkBlockImage from "@app/components/ContentLinkBlockImage.svelte"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
import {PLATFORM_URL, THUMBNAIL_URL} from "@app/env" import {PLATFORM_URL, THUMBNAIL_URL} from "@app/env"
import {AUDIO_CONTENT_TYPES, IMAGE_CONTENT_TYPES, VIDEO_CONTENT_TYPES} from "@app/content" import {
getUrlContentType,
AUDIO_CONTENT_TYPES,
IMAGE_CONTENT_TYPES,
VIDEO_CONTENT_TYPES,
} from "@app/content"
import {isRoomId} from "@app/rooms" import {isRoomId} from "@app/rooms"
const {value, event} = $props() const {value, event} = $props()
@ -41,7 +46,7 @@
return [url, true] return [url, true]
}) })
const fileType = tagValue(tagSpec("file-type"), event.tags) || "" const fileType = getUrlContentType(url, event)
const isAudio = const isAudio =
Boolean(url.match(/\.(mp3|m4a|wav|ogg|oga|opus|flac)$/)) || Boolean(url.match(/\.(mp3|m4a|wav|ogg|oga|opus|flac)$/)) ||

View file

@ -1,26 +1,16 @@
<script lang="ts"> <script lang="ts">
import {onMount, onDestroy} from "svelte" import {onMount, onDestroy} from "svelte"
import {displayUrl, once} from "@welshman/lib" import {displayUrl, once} from "@welshman/lib"
import { import {getBlob, decryptFile, makeBlossomAuthEvent, tagSpec, tagValue} from "@welshman/util"
getBlob,
decryptFile,
makeBlossomAuthEvent,
matchTags,
tagSpec,
tagValue,
} from "@welshman/util"
import LinkRound from "@assets/icons/link-round.svg?dataurl" import LinkRound from "@assets/icons/link-round.svg?dataurl"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import {user} from "@app/core" import {user} from "@app/core"
import {getUrlTags} from "@app/content"
const {value, event, ...props} = $props() const {value, event, ...props} = $props()
const url = value.url.toString() const url = value.url.toString()
// An imeta tag packs its own tags into space-separated values, so unpack the one for this url. const meta = getUrlTags(url, event)
const meta =
matchTags(tagSpec("imeta"), event.tags)
.map(([, ...values]: string[]) => values.map(value => value.split(" ")))
.find(meta => tagValue(tagSpec("url"), meta) === url) || event.tags
// Fallback to filename if hash was omitted from the message for interoperability // Fallback to filename if hash was omitted from the message for interoperability
const hash = tagValue(tagSpec("x"), meta) || url.split(/[\/\.]/).slice(-2)[0] const hash = tagValue(tagSpec("x"), meta) || url.split(/[\/\.]/).slice(-2)[0]

View file

@ -1,18 +1,17 @@
<script lang="ts"> <script lang="ts">
import {displayUrl} from "@welshman/lib" import {displayUrl} from "@welshman/lib"
import {tagSpec, tagValue} from "@welshman/util"
import {preventDefault, stopPropagation} from "@lib/html" import {preventDefault, stopPropagation} from "@lib/html"
import LinkRound from "@assets/icons/link-round.svg?dataurl" import LinkRound from "@assets/icons/link-round.svg?dataurl"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import ContentLinkDetail from "@app/components/ContentLinkDetail.svelte" import ContentLinkDetail from "@app/components/ContentLinkDetail.svelte"
import ContentLinkUrl from "@app/components/ContentLinkUrl.svelte" import ContentLinkUrl from "@app/components/ContentLinkUrl.svelte"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
import {IMAGE_CONTENT_TYPES} from "@app/content" import {getUrlContentType, IMAGE_CONTENT_TYPES} from "@app/content"
const {value, event} = $props() const {value, event} = $props()
const url = value.url.toString() const url = value.url.toString()
const fileType = tagValue(tagSpec("file-type"), event.tags) || "" const fileType = getUrlContentType(url, event)
const expand = () => pushModal(ContentLinkDetail, {value, event}, {fullscreen: true}) const expand = () => pushModal(ContentLinkDetail, {value, event}, {fullscreen: true})
</script> </script>

View file

@ -15,8 +15,11 @@ import {
THREAD, THREAD,
ZAP_GOAL, ZAP_GOAL,
ZAP_RECEIPT, ZAP_RECEIPT,
matchTags,
tagSpec,
tagValue,
} from "@welshman/util" } from "@welshman/util"
import type {Filter} from "@welshman/util" import type {Filter, TrustedEvent} from "@welshman/util"
import {ENABLE_ZAPS} from "@app/env" import {ENABLE_ZAPS} from "@app/env"
export const IMAGE_CONTENT_TYPES = ["image/jpeg", "image/png", "image/gif", "image/webp"] export const IMAGE_CONTENT_TYPES = ["image/jpeg", "image/png", "image/gif", "image/webp"]
@ -32,6 +35,28 @@ export const AUDIO_CONTENT_TYPES = [
"audio/flac", "audio/flac",
] ]
export const getUrlTags = (url: string, event: TrustedEvent) => {
// An imeta tag packs its own tags into space-separated values, so unpack them.
const imetas = matchTags(tagSpec("imeta"), event.tags).map(([, ...values]: string[]) =>
values.map(value => value.split(" ")),
)
const imeta = imetas.find(meta => tagValue(tagSpec("url"), meta) === url)
if (imeta) {
return imeta
}
// A nip 17 file message describes its one file on the event itself, but an event carrying imeta
// has said all it has to say about each url, so any other link in it is described by nothing.
return imetas.length > 0 ? [] : event.tags
}
export const getUrlContentType = (url: string, event: TrustedEvent) => {
const tags = getUrlTags(url, event)
return tagValue(tagSpec("m"), tags) || tagValue(tagSpec("file-type"), tags) || ""
}
export const makeCommentFilter = (kinds: number[], extra: Filter = {}) => ({ export const makeCommentFilter = (kinds: number[], extra: Filter = {}) => ({
kinds: [COMMENT], kinds: [COMMENT],
"#K": kinds.map(String), "#K": kinds.map(String),