Add NIP-30 module for custom emojis
This commit is contained in:
parent
9cd4f16e45
commit
36e0de2a68
2
nip27.ts
2
nip27.ts
|
@ -13,7 +13,7 @@ export interface NostrURIMatch extends NostrURI {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Find and decode all NIP-21 URIs. */
|
/** Find and decode all NIP-21 URIs. */
|
||||||
export function* matchAll(content: string): Iterable<NostrURIMatch> {
|
export function * matchAll(content: string): Iterable<NostrURIMatch> {
|
||||||
const matches = content.matchAll(regex())
|
const matches = content.matchAll(regex())
|
||||||
|
|
||||||
for (const match of matches) {
|
for (const match of matches) {
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import {matchAll, replaceAll} from './nip30.ts'
|
||||||
|
|
||||||
|
test('matchAll', () => {
|
||||||
|
const result = matchAll('Hello :blobcat: :disputed: ::joy:joy:')
|
||||||
|
|
||||||
|
expect([...result]).toEqual([
|
||||||
|
{
|
||||||
|
name: 'blobcat',
|
||||||
|
shortcode: ':blobcat:',
|
||||||
|
start: 6,
|
||||||
|
end: 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'disputed',
|
||||||
|
shortcode: ':disputed:',
|
||||||
|
start: 16,
|
||||||
|
end: 26
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('replaceAll', () => {
|
||||||
|
const content = 'Hello :blobcat: :disputed: ::joy:joy:'
|
||||||
|
|
||||||
|
const result = replaceAll(content, ({name}) => {
|
||||||
|
return `<img src="https://ditto.pub/emoji/${name}.png" />`
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual(
|
||||||
|
'Hello <img src="https://ditto.pub/emoji/blobcat.png" /> <img src="https://ditto.pub/emoji/disputed.png" /> ::joy:joy:',
|
||||||
|
)
|
||||||
|
})
|
|
@ -0,0 +1,54 @@
|
||||||
|
/** Regex for a single emoji shortcode. */
|
||||||
|
export const EMOJI_SHORTCODE_REGEX = /:(\w+):/
|
||||||
|
|
||||||
|
/** Regex to find emoji shortcodes in content. */
|
||||||
|
export const regex = () => new RegExp(`\\B${EMOJI_SHORTCODE_REGEX.source}\\B`, 'g')
|
||||||
|
|
||||||
|
/** Represents a Nostr custom emoji. */
|
||||||
|
export interface CustomEmoji {
|
||||||
|
/** The matched emoji name with colons. */
|
||||||
|
shortcode: `:${string}:`
|
||||||
|
/** The matched emoji name without colons. */
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Match result for a custom emoji in text content. */
|
||||||
|
export interface CustomEmojiMatch extends CustomEmoji {
|
||||||
|
/** Index where the emoji begins in the text content. */
|
||||||
|
start: number
|
||||||
|
/** Index where the emoji ends in the text content. */
|
||||||
|
end: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Find all custom emoji shortcodes. */
|
||||||
|
export function * matchAll(content: string): Iterable<CustomEmojiMatch> {
|
||||||
|
const matches = content.matchAll(regex())
|
||||||
|
|
||||||
|
for (const match of matches) {
|
||||||
|
try {
|
||||||
|
const [shortcode, name] = match
|
||||||
|
|
||||||
|
yield {
|
||||||
|
shortcode: shortcode as `:${string}:`,
|
||||||
|
name,
|
||||||
|
start: match.index!,
|
||||||
|
end: match.index! + shortcode.length
|
||||||
|
}
|
||||||
|
} catch (_e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Replace all emoji shortcodes in the content. */
|
||||||
|
export function replaceAll(
|
||||||
|
content: string,
|
||||||
|
replacer: (match: CustomEmoji) => string
|
||||||
|
): string {
|
||||||
|
return content.replaceAll(regex(), (shortcode, name) => {
|
||||||
|
return replacer({
|
||||||
|
shortcode: shortcode as `:${string}:`,
|
||||||
|
name,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue