feat: implement room and space mentions (#130) #154

Closed
Khushvendra wants to merge 5 commits from Khushvendra/flotilla:feat/room-mentions-130 into dev
5 changed files with 79 additions and 29 deletions
Showing only changes of commit 540e9abe3d - Show all commits
+33 -12
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import {call, ellipsize, displayUrl, postJson} from "@welshman/lib"
import {isRelayUrl, getTagValue, normalizeRelayUrl} from "@welshman/util"
import {isRelayUrl, getTagValue, normalizeRelayUrl, displayRelayUrl} from "@welshman/util"
import {Capacitor} from "@capacitor/core"
import {preventDefault, stopPropagation} from "@lib/html"
import LinkRound from "@assets/icons/link-round.svg?dataurl"
@@ -15,6 +15,9 @@
IMAGE_CONTENT_TYPES,
VIDEO_CONTENT_TYPES,
THUMBNAIL_URL,
displayRoom,
isRoomId,
splitRoomId,
} from "@app/core/state"
import {makeRoomPath, makeSpacePath} from "@app/util/routes"
@@ -23,25 +26,43 @@
let hideImage = $state(false)
const url = value.url.toString()
const splitRoomReference = (input: string) => {
const separatorIndex = input.indexOf("'")
if (separatorIndex === -1) {
const roomReference = call(() => {
if (!isRoomId(url)) {
return undefined
}
const roomUrl = input.slice(0, separatorIndex)
const h = input.slice(separatorIndex + 1)
const [roomUrl, h] = splitRoomId(url)
Khushvendra marked this conversation as resolved Outdated
Outdated
Review

I think we can live dangerously here by adding isRoomId to core/state which just checks for an apostrophe, and use splitRoomId from that same file.

I think we can live dangerously here by adding `isRoomId` to core/state which just checks for an apostrophe, and use `splitRoomId` from that same file.
if (!h || !isRelayUrl(roomUrl)) {
if (!roomUrl || !h || !isRelayUrl(roomUrl)) {
return undefined
}
return {url: normalizeRelayUrl(roomUrl), h}
}
})
const relayReference = call(() => {
if (roomReference || !isRelayUrl(url)) {
return undefined
}
return normalizeRelayUrl(url)
})
const label = call(() => {
if (roomReference) {
const spaceName = displayRelayUrl(roomReference.url)
const roomName = displayRoom(roomReference.url, roomReference.h)
return `~${spaceName} / ${roomName}`
}
if (relayReference) {
hodlbod marked this conversation as resolved
Review

It doesn't make sense to use ContentLinkUrl in this case since because we're providing our own content, it's doing basically nothing. Remove the children and showIcon props from ContentLinkUrl. You can might be able to remove the labelClass as well, not sure.

It doesn't make sense to use ContentLinkUrl in this case since because we're providing our own content, it's doing basically nothing. Remove the children and showIcon props from ContentLinkUrl. You can might be able to remove the labelClass as well, not sure.
return `~${displayRelayUrl(relayReference)}`
}
return displayUrl(url)
})
const roomReference = splitRoomReference(url)
const relayReference = !roomReference && isRelayUrl(url) ? normalizeRelayUrl(url) : undefined
const fileType = getTagValue("file-type", event.tags) || ""
const [href, external] = call(() => {
if (roomReference) return [makeRoomPath(roomReference.url, roomReference.h), false]
1
@@ -94,7 +115,7 @@
{:else if roomReference || relayReference}
<div class="bg-alt p-4 leading-normal">
<Icon icon={LinkRound} size={3} class="inline-block" />
<span class="ml-2">{displayUrl(url)}</span>
<span class="ml-2">{label}</span>
</div>
{:else}
{#await loadPreview()}
+38 -13
View File
@@ -1,40 +1,65 @@
<script lang="ts">
import {call, displayUrl} from "@welshman/lib"
import {isRelayUrl, getTagValue, normalizeRelayUrl} from "@welshman/util"
import {isRelayUrl, getTagValue, normalizeRelayUrl, displayRelayUrl} from "@welshman/util"
import {preventDefault, stopPropagation} from "@lib/html"
import LinkRound from "@assets/icons/link-round.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
import Link from "@lib/components/Link.svelte"
import ContentLinkDetail from "@app/components/ContentLinkDetail.svelte"
import {pushModal} from "@app/util/modal"
import {PLATFORM_URL, IMAGE_CONTENT_TYPES} from "@app/core/state"
import {
PLATFORM_URL,
IMAGE_CONTENT_TYPES,
displayRoom,
isRoomId,
splitRoomId,
} from "@app/core/state"
import {makeRoomPath, makeSpacePath} from "@app/util/routes"
const {value, event} = $props()
const url = value.url.toString()
const splitRoomReference = (input: string) => {
const separatorIndex = input.indexOf("'")
if (separatorIndex === -1) {
const roomReference = call(() => {
if (!isRoomId(url)) {
return undefined
}
const roomUrl = input.slice(0, separatorIndex)
const h = input.slice(separatorIndex + 1)
const [roomUrl, h] = splitRoomId(url)
if (!h || !isRelayUrl(roomUrl)) {
if (!roomUrl || !h || !isRelayUrl(roomUrl)) {
return undefined
}
return {url: normalizeRelayUrl(roomUrl), h}
}
})
const relayReference = call(() => {
if (roomReference || !isRelayUrl(url)) {
return undefined
}
return normalizeRelayUrl(url)
})
const label = call(() => {
if (roomReference) {
const spaceName = displayRelayUrl(roomReference.url)
const roomName = displayRoom(roomReference.url, roomReference.h)
return `~${spaceName} / ${roomName}`
}
if (relayReference) {
return `~${displayRelayUrl(relayReference)}`
}
return displayUrl(url)
})
Khushvendra marked this conversation as resolved Outdated
Outdated
Review

Let's refactor this a bit. Go ahead and add a new component called ContentLinkUrl that encapsulates this logic and is used in both inline and block contexts. In the block case you'll still need to detect whether the url is a room or relay, but that should be as simple as isRoomId(url) || isRelayUrl(url)

Let's refactor this a bit. Go ahead and add a new component called `ContentLinkUrl` that encapsulates this logic and is used in both inline and block contexts. In the block case you'll still need to detect whether the url is a room or relay, but that should be as simple as `isRoomId(url) || isRelayUrl(url)`
const roomReference = splitRoomReference(url)
const fileType = getTagValue("file-type", event.tags) || ""
const [href, external] = call(() => {
if (roomReference) return [makeRoomPath(roomReference.url, roomReference.h), false]
if (isRelayUrl(url)) return [makeSpacePath(normalizeRelayUrl(url)), false]
if (relayReference) return [makeSpacePath(relayReference), false]
if (url.startsWith(PLATFORM_URL)) return [url.replace(PLATFORM_URL, ""), false]
return [url, true]
@@ -55,6 +80,6 @@
{:else}
<Link {external} {href} class="link-content whitespace-nowrap">
<Icon icon={LinkRound} size={3} class="inline-block" />
{displayUrl(url)}
{label}
</Link>
{/if}
+2
View File
@@ -596,6 +596,8 @@ export const getRoomType = (room: RoomMeta): RoomType =>
export const makeRoomId = (url: string, h: string) => `${url}'${h}`
export const isRoomId = (id: string) => id.includes("'")
export const splitRoomId = (id: string) => id.split("'")
export const hasNip29 = (relay?: RelayProfile) =>
+2 -1
View File
@@ -1,4 +1,5 @@
import type {NodeViewRendererProps} from "@tiptap/core"
import {displayRelayUrl} from "@welshman/util"
import {deriveRoom} from "@app/core/state"
export const RoomReferenceNodeView = ({node}: NodeViewRendererProps) => {
@@ -10,7 +11,7 @@ export const RoomReferenceNodeView = ({node}: NodeViewRendererProps) => {
dom.classList.add("tiptap-object")
const unsubRoom = room.subscribe($room => {
dom.textContent = `~${$room.name || h}`
dom.textContent = `~${displayRelayUrl(url)} / ${$room.name || h}`
})
return {
+4 -3
View File
@@ -9,9 +9,10 @@
const {value}: Props = $props()
const [url = "", h = ""] = splitRoomId(value)
const room = deriveRoom(url, h)
const label = $derived(`~${displayRelayUrl(url)} / ${$room.name || h}`)
</script>
<div class="flex max-w-full flex-col gap-1">
<div class="overflow-hidden text-ellipsis text-base font-semibold">~{$room.name || h}</div>
<div class="overflow-hidden text-ellipsis text-sm opacity-75">{displayRelayUrl(url)}'{h}</div>
<div class="max-w-full overflow-hidden text-ellipsis text-base font-semibold">
Khushvendra marked this conversation as resolved Outdated
Outdated
Review

This should be displayed the same way we'll ultimately render it, as ~Space Name / Room Name

This should be displayed the same way we'll ultimately render it, as `~Space Name / Room Name`
{label}
</div>