refactor: extract tag building functions
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { ApplicationDataKey, ExtendedKind } from '@/constants'
|
import { ApplicationDataKey, ExtendedKind } from '@/constants'
|
||||||
import client from '@/services/client.service'
|
import client from '@/services/client.service'
|
||||||
import mediaUpload from '@/services/media-upload.service'
|
import mediaUpload from '@/services/media-upload.service'
|
||||||
import { TDraftEvent, TEmoji, TMailboxRelay, TRelaySet } from '@/types'
|
import { TDraftEvent, TEmoji, TMailboxRelay, TMailboxRelayScope, TRelaySet } from '@/types'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { Event, kinds, nip19 } from 'nostr-tools'
|
import { Event, kinds, nip19 } from 'nostr-tools'
|
||||||
import {
|
import {
|
||||||
@@ -11,24 +11,18 @@ import {
|
|||||||
isReplaceableEvent
|
isReplaceableEvent
|
||||||
} from './event'
|
} from './event'
|
||||||
import { generateBech32IdFromETag, tagNameEquals } from './tag'
|
import { generateBech32IdFromETag, tagNameEquals } from './tag'
|
||||||
import { normalizeHttpUrl } from './url'
|
|
||||||
|
|
||||||
// https://github.com/nostr-protocol/nips/blob/master/25.md
|
// https://github.com/nostr-protocol/nips/blob/master/25.md
|
||||||
export function createReactionDraftEvent(event: Event, emoji: TEmoji | string = '+'): TDraftEvent {
|
export function createReactionDraftEvent(event: Event, emoji: TEmoji | string = '+'): TDraftEvent {
|
||||||
const tags: string[][] = []
|
const tags: string[][] = []
|
||||||
const hint = client.getEventHint(event.id)
|
tags.push(buildETag(event.id, event.pubkey))
|
||||||
tags.push(['e', event.id, hint, event.pubkey])
|
tags.push(buildPTag(event.pubkey))
|
||||||
tags.push(['p', event.pubkey])
|
|
||||||
if (event.kind !== kinds.ShortTextNote) {
|
if (event.kind !== kinds.ShortTextNote) {
|
||||||
tags.push(['k', event.kind.toString()])
|
tags.push(buildKTag(event.kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isReplaceableEvent(event.kind)) {
|
if (isReplaceableEvent(event.kind)) {
|
||||||
tags.push(
|
tags.push(buildATag(event))
|
||||||
hint
|
|
||||||
? ['a', getReplaceableEventCoordinate(event), hint]
|
|
||||||
: ['a', getReplaceableEventCoordinate(event)]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let content: string
|
let content: string
|
||||||
@@ -36,7 +30,7 @@ export function createReactionDraftEvent(event: Event, emoji: TEmoji | string =
|
|||||||
content = emoji
|
content = emoji
|
||||||
} else {
|
} else {
|
||||||
content = `:${emoji.shortcode}:`
|
content = `:${emoji.shortcode}:`
|
||||||
tags.push(['emoji', emoji.shortcode, emoji.url])
|
tags.push(buildEmojiTag(emoji))
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -50,10 +44,7 @@ export function createReactionDraftEvent(event: Event, emoji: TEmoji | string =
|
|||||||
// https://github.com/nostr-protocol/nips/blob/master/18.md
|
// https://github.com/nostr-protocol/nips/blob/master/18.md
|
||||||
export function createRepostDraftEvent(event: Event): TDraftEvent {
|
export function createRepostDraftEvent(event: Event): TDraftEvent {
|
||||||
const isProtected = isProtectedEvent(event)
|
const isProtected = isProtectedEvent(event)
|
||||||
const tags = [
|
const tags = [buildETag(event.id, event.pubkey), buildPTag(event.pubkey)]
|
||||||
['e', event.id, client.getEventHint(event.id), '', event.pubkey],
|
|
||||||
['p', event.pubkey]
|
|
||||||
]
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: kinds.Repost,
|
kind: kinds.Repost,
|
||||||
@@ -80,7 +71,7 @@ export async function createShortTextNoteDraftEvent(
|
|||||||
)
|
)
|
||||||
const hashtags = extractHashtags(content)
|
const hashtags = extractHashtags(content)
|
||||||
|
|
||||||
const tags = hashtags.map((hashtag) => ['t', hashtag])
|
const tags = hashtags.map((hashtag) => buildTTag(hashtag))
|
||||||
|
|
||||||
// imeta tags
|
// imeta tags
|
||||||
const images = extractImagesFromContent(content)
|
const images = extractImagesFromContent(content)
|
||||||
@@ -89,7 +80,7 @@ export async function createShortTextNoteDraftEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// q tags
|
// q tags
|
||||||
tags.push(...quoteEventIds.map((eventId) => ['q', eventId, client.getEventHint(eventId)]))
|
tags.push(...quoteEventIds.map((eventId) => buildQTag(eventId)))
|
||||||
|
|
||||||
// e tags
|
// e tags
|
||||||
if (rootETag.length) {
|
if (rootETag.length) {
|
||||||
@@ -101,18 +92,18 @@ export async function createShortTextNoteDraftEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// p tags
|
// p tags
|
||||||
tags.push(...mentions.map((pubkey) => ['p', pubkey]))
|
tags.push(...mentions.map((pubkey) => buildPTag(pubkey)))
|
||||||
|
|
||||||
if (options.addClientTag) {
|
if (options.addClientTag) {
|
||||||
tags.push(['client', 'jumble'])
|
tags.push(buildClientTag())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.isNsfw) {
|
if (options.isNsfw) {
|
||||||
tags.push(['content-warning', 'NSFW'])
|
tags.push(buildNsfwTag())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.protectedEvent) {
|
if (options.protectedEvent) {
|
||||||
tags.push(['-'])
|
tags.push(buildProtectedTag())
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseDraft = {
|
const baseDraft = {
|
||||||
@@ -137,9 +128,9 @@ export function createRelaySetDraftEvent(relaySet: TRelaySet): TDraftEvent {
|
|||||||
kind: kinds.Relaysets,
|
kind: kinds.Relaysets,
|
||||||
content: '',
|
content: '',
|
||||||
tags: [
|
tags: [
|
||||||
['d', relaySet.id],
|
buildDTag(relaySet.id),
|
||||||
['title', relaySet.name],
|
buildTitleTag(relaySet.name),
|
||||||
...relaySet.relayUrls.map((url) => ['relay', url])
|
...relaySet.relayUrls.map((url) => buildRelayTag(url))
|
||||||
],
|
],
|
||||||
created_at: dayjs().unix()
|
created_at: dayjs().unix()
|
||||||
}
|
}
|
||||||
@@ -161,17 +152,17 @@ export async function createPictureNoteDraftEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tags = pictureInfos
|
const tags = pictureInfos
|
||||||
.map((info) => ['imeta', ...info.tags.map(([n, v]) => `${n} ${v}`)])
|
.map((info) => buildImetaTag(info.tags))
|
||||||
.concat(hashtags.map((hashtag) => ['t', hashtag]))
|
.concat(hashtags.map((hashtag) => buildTTag(hashtag)))
|
||||||
.concat(quoteEventIds.map((eventId) => ['q', eventId, client.getEventHint(eventId)]))
|
.concat(quoteEventIds.map((eventId) => buildQTag(eventId)))
|
||||||
.concat(mentions.map((pubkey) => ['p', pubkey]))
|
.concat(mentions.map((pubkey) => buildPTag(pubkey)))
|
||||||
|
|
||||||
if (options.addClientTag) {
|
if (options.addClientTag) {
|
||||||
tags.push(['client', 'jumble'])
|
tags.push(buildClientTag())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.protectedEvent) {
|
if (options.protectedEvent) {
|
||||||
tags.push(['-'])
|
tags.push(buildProtectedTag())
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -193,69 +184,57 @@ export async function createCommentDraftEvent(
|
|||||||
isNsfw?: boolean
|
isNsfw?: boolean
|
||||||
} = {}
|
} = {}
|
||||||
): Promise<TDraftEvent> {
|
): Promise<TDraftEvent> {
|
||||||
const {
|
const { quoteEventIds, rootEventId, rootCoordinateTag, rootKind, rootPubkey, rootUrl } =
|
||||||
quoteEventIds,
|
await extractCommentMentions(content, parentEvent)
|
||||||
rootEventId,
|
|
||||||
rootCoordinateTag,
|
|
||||||
rootKind,
|
|
||||||
rootPubkey,
|
|
||||||
rootUrl,
|
|
||||||
parentEventId,
|
|
||||||
parentCoordinate,
|
|
||||||
parentKind,
|
|
||||||
parentPubkey
|
|
||||||
} = await extractCommentMentions(content, parentEvent)
|
|
||||||
const hashtags = extractHashtags(content)
|
const hashtags = extractHashtags(content)
|
||||||
|
|
||||||
const tags = hashtags
|
const tags = hashtags
|
||||||
.map((hashtag) => ['t', hashtag])
|
.map((hashtag) => buildTTag(hashtag))
|
||||||
.concat(quoteEventIds.map((eventId) => ['q', eventId, client.getEventHint(eventId)]))
|
.concat(quoteEventIds.map((eventId) => buildQTag(eventId)))
|
||||||
|
|
||||||
const images = extractImagesFromContent(content)
|
const images = extractImagesFromContent(content)
|
||||||
if (images && images.length) {
|
if (images && images.length) {
|
||||||
tags.push(...generateImetaTags(images))
|
tags.push(...generateImetaTags(images))
|
||||||
}
|
}
|
||||||
|
|
||||||
tags.push(...mentions.filter((pubkey) => pubkey !== parentPubkey).map((pubkey) => ['p', pubkey]))
|
tags.push(
|
||||||
|
...mentions.filter((pubkey) => pubkey !== parentEvent.pubkey).map((pubkey) => buildPTag(pubkey))
|
||||||
|
)
|
||||||
|
|
||||||
if (rootCoordinateTag) {
|
if (rootCoordinateTag) {
|
||||||
tags.push(rootCoordinateTag)
|
tags.push(rootCoordinateTag)
|
||||||
} else if (rootEventId) {
|
} else if (rootEventId) {
|
||||||
tags.push(
|
tags.push(buildETag(rootEventId, rootPubkey, '', true))
|
||||||
rootPubkey
|
|
||||||
? ['E', rootEventId, client.getEventHint(rootEventId), rootPubkey]
|
|
||||||
: ['E', rootEventId, client.getEventHint(rootEventId)]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
if (rootPubkey) {
|
if (rootPubkey) {
|
||||||
tags.push(['P', rootPubkey])
|
tags.push(buildPTag(rootPubkey, true))
|
||||||
}
|
}
|
||||||
if (rootKind) {
|
if (rootKind) {
|
||||||
tags.push(['K', rootKind.toString()])
|
tags.push(buildKTag(rootKind, true))
|
||||||
}
|
}
|
||||||
if (rootUrl) {
|
if (rootUrl) {
|
||||||
tags.push(['I', rootUrl])
|
tags.push(buildITag(rootUrl, true))
|
||||||
}
|
}
|
||||||
tags.push(
|
tags.push(
|
||||||
...[
|
...[
|
||||||
parentCoordinate
|
isReplaceableEvent(parentEvent.kind)
|
||||||
? ['a', parentCoordinate, client.getEventHint(parentEventId)]
|
? buildATag(parentEvent)
|
||||||
: ['e', parentEventId, client.getEventHint(parentEventId), parentPubkey],
|
: buildETag(parentEvent.id, parentEvent.pubkey),
|
||||||
['k', parentKind.toString()],
|
buildKTag(parentEvent.kind),
|
||||||
['p', parentPubkey]
|
buildPTag(parentEvent.pubkey)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
if (options.addClientTag) {
|
if (options.addClientTag) {
|
||||||
tags.push(['client', 'jumble'])
|
tags.push(buildClientTag())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.isNsfw) {
|
if (options.isNsfw) {
|
||||||
tags.push(['content-warning', 'NSFW'])
|
tags.push(buildNsfwTag())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.protectedEvent) {
|
if (options.protectedEvent) {
|
||||||
tags.push(['-'])
|
tags.push(buildProtectedTag())
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseDraft = {
|
const baseDraft = {
|
||||||
@@ -278,9 +257,7 @@ export function createRelayListDraftEvent(mailboxRelays: TMailboxRelay[]): TDraf
|
|||||||
return {
|
return {
|
||||||
kind: kinds.RelayList,
|
kind: kinds.RelayList,
|
||||||
content: '',
|
content: '',
|
||||||
tags: mailboxRelays.map(({ url, scope }) =>
|
tags: mailboxRelays.map(({ url, scope }) => buildRTag(url, scope)),
|
||||||
scope === 'both' ? ['r', url] : ['r', url, scope]
|
|
||||||
),
|
|
||||||
created_at: dayjs().unix()
|
created_at: dayjs().unix()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,10 +295,10 @@ export function createFavoriteRelaysDraftEvent(
|
|||||||
): TDraftEvent {
|
): TDraftEvent {
|
||||||
const tags: string[][] = []
|
const tags: string[][] = []
|
||||||
favoriteRelays.forEach((url) => {
|
favoriteRelays.forEach((url) => {
|
||||||
tags.push(['relay', url])
|
tags.push(buildRelayTag(url))
|
||||||
})
|
})
|
||||||
relaySetEvents.forEach((event) => {
|
relaySetEvents.forEach((event) => {
|
||||||
tags.push(['a', getReplaceableEventCoordinate(event)])
|
tags.push(buildATag(event))
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
kind: ExtendedKind.FAVORITE_RELAYS,
|
kind: ExtendedKind.FAVORITE_RELAYS,
|
||||||
@@ -335,7 +312,7 @@ export function createSeenNotificationsAtDraftEvent(): TDraftEvent {
|
|||||||
return {
|
return {
|
||||||
kind: kinds.Application,
|
kind: kinds.Application,
|
||||||
content: 'Records read time to sync notification status across devices.',
|
content: 'Records read time to sync notification status across devices.',
|
||||||
tags: [['d', ApplicationDataKey.NOTIFICATIONS_SEEN_AT]],
|
tags: [buildDTag(ApplicationDataKey.NOTIFICATIONS_SEEN_AT)],
|
||||||
created_at: dayjs().unix()
|
created_at: dayjs().unix()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -353,7 +330,7 @@ export function createBlossomServerListDraftEvent(servers: string[]): TDraftEven
|
|||||||
return {
|
return {
|
||||||
kind: ExtendedKind.BLOSSOM_SERVER_LIST,
|
kind: ExtendedKind.BLOSSOM_SERVER_LIST,
|
||||||
content: '',
|
content: '',
|
||||||
tags: servers.map((server) => ['server', normalizeHttpUrl(server)]),
|
tags: servers.map((server) => buildServerTag(server)),
|
||||||
created_at: dayjs().unix()
|
created_at: dayjs().unix()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -394,39 +371,21 @@ async function extractRelatedEventIds(content: string, parentEvent?: Event) {
|
|||||||
if (parentEvent) {
|
if (parentEvent) {
|
||||||
const _rootETag = getRootETag(parentEvent)
|
const _rootETag = getRootETag(parentEvent)
|
||||||
if (_rootETag) {
|
if (_rootETag) {
|
||||||
parentETag = [
|
parentETag = buildETagWithMarker(parentEvent.id, parentEvent.pubkey, '', 'reply')
|
||||||
'e',
|
|
||||||
parentEvent.id,
|
|
||||||
client.getEventHint(parentEvent.id),
|
|
||||||
'reply',
|
|
||||||
parentEvent.pubkey
|
|
||||||
]
|
|
||||||
|
|
||||||
const [, rootEventHexId, hint, , rootEventPubkey] = _rootETag
|
const [, rootEventHexId, hint, , rootEventPubkey] = _rootETag
|
||||||
if (rootEventPubkey) {
|
if (rootEventPubkey) {
|
||||||
rootETag = [
|
rootETag = buildETagWithMarker(rootEventHexId, rootEventPubkey, hint, 'root')
|
||||||
'e',
|
|
||||||
rootEventHexId,
|
|
||||||
hint ?? client.getEventHint(rootEventHexId),
|
|
||||||
'root',
|
|
||||||
rootEventPubkey
|
|
||||||
]
|
|
||||||
} else {
|
} else {
|
||||||
const rootEventId = generateBech32IdFromETag(_rootETag)
|
const rootEventId = generateBech32IdFromETag(_rootETag)
|
||||||
const rootEvent = rootEventId ? await client.fetchEvent(rootEventId) : undefined
|
const rootEvent = rootEventId ? await client.fetchEvent(rootEventId) : undefined
|
||||||
rootETag = rootEvent
|
rootETag = rootEvent
|
||||||
? ['e', rootEvent.id, hint ?? client.getEventHint(rootEvent.id), 'root', rootEvent.pubkey]
|
? buildETagWithMarker(rootEvent.id, rootEvent.pubkey, hint, 'root')
|
||||||
: ['e', rootEventHexId, hint ?? client.getEventHint(rootEventHexId), 'root']
|
: buildETagWithMarker(rootEventHexId, rootEventPubkey, hint, 'root')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// reply to root event
|
// reply to root event
|
||||||
rootETag = [
|
rootETag = buildETagWithMarker(parentEvent.id, parentEvent.pubkey, '', 'root')
|
||||||
'e',
|
|
||||||
parentEvent.id,
|
|
||||||
client.getEventHint(parentEvent.id),
|
|
||||||
'root',
|
|
||||||
parentEvent.pubkey
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,12 +398,11 @@ async function extractRelatedEventIds(content: string, parentEvent?: Event) {
|
|||||||
|
|
||||||
async function extractCommentMentions(content: string, parentEvent: Event) {
|
async function extractCommentMentions(content: string, parentEvent: Event) {
|
||||||
const quoteEventIds: string[] = []
|
const quoteEventIds: string[] = []
|
||||||
const parentEventIsReplaceable = isReplaceableEvent(parentEvent.kind)
|
|
||||||
const rootCoordinateTag =
|
const rootCoordinateTag =
|
||||||
parentEvent.kind === ExtendedKind.COMMENT
|
parentEvent.kind === ExtendedKind.COMMENT
|
||||||
? parentEvent.tags.find(tagNameEquals('A'))
|
? parentEvent.tags.find(tagNameEquals('A'))
|
||||||
: isReplaceableEvent(parentEvent.kind)
|
: isReplaceableEvent(parentEvent.kind)
|
||||||
? ['A', getReplaceableEventCoordinate(parentEvent), client.getEventHint(parentEvent.id)]
|
? buildATag(parentEvent, true)
|
||||||
: undefined
|
: undefined
|
||||||
const rootEventId =
|
const rootEventId =
|
||||||
parentEvent.kind === ExtendedKind.COMMENT
|
parentEvent.kind === ExtendedKind.COMMENT
|
||||||
@@ -463,13 +421,6 @@ async function extractCommentMentions(content: string, parentEvent: Event) {
|
|||||||
? parentEvent.tags.find(tagNameEquals('I'))?.[1]
|
? parentEvent.tags.find(tagNameEquals('I'))?.[1]
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
const parentEventId = parentEvent.id
|
|
||||||
const parentCoordinate = parentEventIsReplaceable
|
|
||||||
? getReplaceableEventCoordinate(parentEvent)
|
|
||||||
: undefined
|
|
||||||
const parentKind = parentEvent.kind
|
|
||||||
const parentPubkey = parentEvent.pubkey
|
|
||||||
|
|
||||||
const addToSet = (arr: string[], item: string) => {
|
const addToSet = (arr: string[], item: string) => {
|
||||||
if (!arr.includes(item)) arr.push(item)
|
if (!arr.includes(item)) arr.push(item)
|
||||||
}
|
}
|
||||||
@@ -496,10 +447,7 @@ async function extractCommentMentions(content: string, parentEvent: Event) {
|
|||||||
rootKind,
|
rootKind,
|
||||||
rootPubkey,
|
rootPubkey,
|
||||||
rootUrl,
|
rootUrl,
|
||||||
parentEventId,
|
parentEvent
|
||||||
parentCoordinate,
|
|
||||||
parentKind,
|
|
||||||
parentPubkey
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,3 +466,102 @@ function extractHashtags(content: string) {
|
|||||||
function extractImagesFromContent(content: string) {
|
function extractImagesFromContent(content: string) {
|
||||||
return content.match(/https?:\/\/[^\s"']+\.(jpg|jpeg|png|gif|webp|heic)/gi)
|
return content.match(/https?:\/\/[^\s"']+\.(jpg|jpeg|png|gif|webp|heic)/gi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildATag(event: Event, upperCase: boolean = false) {
|
||||||
|
const coordinate = getReplaceableEventCoordinate(event)
|
||||||
|
const hint = client.getEventHint(event.id)
|
||||||
|
return trimTagEnd([upperCase ? 'A' : 'a', coordinate, hint])
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDTag(identifier: string) {
|
||||||
|
return ['d', identifier]
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildETag(
|
||||||
|
eventHexId: string,
|
||||||
|
pubkey: string = '',
|
||||||
|
hint: string = '',
|
||||||
|
upperCase: boolean = false
|
||||||
|
) {
|
||||||
|
if (!hint) {
|
||||||
|
hint = client.getEventHint(eventHexId)
|
||||||
|
}
|
||||||
|
return trimTagEnd([upperCase ? 'E' : 'e', eventHexId, hint, pubkey])
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildETagWithMarker(
|
||||||
|
eventHexId: string,
|
||||||
|
pubkey: string = '',
|
||||||
|
hint: string = '',
|
||||||
|
marker: 'root' | 'reply' | '' = ''
|
||||||
|
) {
|
||||||
|
if (!hint) {
|
||||||
|
hint = client.getEventHint(eventHexId)
|
||||||
|
}
|
||||||
|
return trimTagEnd(['e', eventHexId, hint, marker, pubkey])
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildITag(url: string, upperCase: boolean = false) {
|
||||||
|
return [upperCase ? 'I' : 'i', url]
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildKTag(kind: number | string, upperCase: boolean = false) {
|
||||||
|
return [upperCase ? 'K' : 'k', kind.toString()]
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPTag(pubkey: string, upperCase: boolean = false) {
|
||||||
|
return [upperCase ? 'P' : 'p', pubkey]
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildQTag(eventHexId: string) {
|
||||||
|
return trimTagEnd(['q', eventHexId, client.getEventHint(eventHexId)]) // TODO: pubkey
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRTag(url: string, scope: TMailboxRelayScope) {
|
||||||
|
return scope === 'both' ? ['r', url, scope] : ['r', url]
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildTTag(hashtag: string) {
|
||||||
|
return ['t', hashtag]
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildEmojiTag(emoji: TEmoji) {
|
||||||
|
return ['emoji', emoji.shortcode, emoji.url]
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildTitleTag(title: string) {
|
||||||
|
return ['title', title]
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRelayTag(url: string) {
|
||||||
|
return ['relay', url]
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildServerTag(url: string) {
|
||||||
|
return ['server', url]
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildImetaTag(nip94Tags: string[][]) {
|
||||||
|
return ['imeta', ...nip94Tags.map(([n, v]) => `${n} ${v}`)]
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildClientTag() {
|
||||||
|
return ['client', 'jumble']
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildNsfwTag() {
|
||||||
|
return ['content-warning', 'NSFW']
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildProtectedTag() {
|
||||||
|
return ['-']
|
||||||
|
}
|
||||||
|
|
||||||
|
function trimTagEnd(tag: string[]) {
|
||||||
|
let endIndex = tag.length - 1
|
||||||
|
while (endIndex >= 0 && tag[endIndex] === '') {
|
||||||
|
endIndex--
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag.slice(0, endIndex + 1)
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Separator } from '@/components/ui/separator'
|
|||||||
import { RECOMMENDED_BLOSSOM_SERVERS } from '@/constants'
|
import { RECOMMENDED_BLOSSOM_SERVERS } from '@/constants'
|
||||||
import { createBlossomServerListDraftEvent } from '@/lib/draft-event'
|
import { createBlossomServerListDraftEvent } from '@/lib/draft-event'
|
||||||
import { getServersFromServerTags } from '@/lib/tag'
|
import { getServersFromServerTags } from '@/lib/tag'
|
||||||
|
import { normalizeHttpUrl } from '@/lib/url'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { useNostr } from '@/providers/NostrProvider'
|
import { useNostr } from '@/providers/NostrProvider'
|
||||||
import client from '@/services/client.service'
|
import client from '@/services/client.service'
|
||||||
@@ -56,7 +57,9 @@ export default function BlossomServerListSetting() {
|
|||||||
const handleUrlInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
const handleUrlInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
addBlossomUrl(url)
|
const normalizedUrl = normalizeHttpUrl(url.trim())
|
||||||
|
if (!normalizedUrl) return
|
||||||
|
addBlossomUrl(normalizedUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +170,15 @@ export default function BlossomServerListSetting() {
|
|||||||
placeholder={t('Enter Blossom server URL')}
|
placeholder={t('Enter Blossom server URL')}
|
||||||
onKeyDown={handleUrlInputKeyDown}
|
onKeyDown={handleUrlInputKeyDown}
|
||||||
/>
|
/>
|
||||||
<Button type="button" onClick={() => addBlossomUrl(url)} title={t('Add')}>
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
const normalizedUrl = normalizeHttpUrl(url.trim())
|
||||||
|
if (!normalizedUrl) return
|
||||||
|
addBlossomUrl(normalizedUrl)
|
||||||
|
}}
|
||||||
|
title={t('Add')}
|
||||||
|
>
|
||||||
{adding && <Loader className="animate-spin" />}
|
{adding && <Loader className="animate-spin" />}
|
||||||
{t('Add')}
|
{t('Add')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user