10 changed files with 245 additions and 40 deletions
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
import { describe, expect, it } from 'vitest' |
||||
import { |
||||
formatPaytoLinkDisplayText, |
||||
paytoLinkChildTextLooksLikeAuthority, |
||||
truncatePaytoAuthority |
||||
} from './payto-display' |
||||
|
||||
describe('truncatePaytoAuthority', () => { |
||||
it('returns full string when within limit', () => { |
||||
expect(truncatePaytoAuthority('derjuergen')).toBe('derjuergen') |
||||
}) |
||||
|
||||
it('truncates to 10 characters', () => { |
||||
expect(truncatePaytoAuthority('47R4NpvudmrLkLxaf4Uy')).toBe('47R4Npvudm') |
||||
}) |
||||
}) |
||||
|
||||
describe('formatPaytoLinkDisplayText', () => { |
||||
it('formats long monero address with label', () => { |
||||
expect( |
||||
formatPaytoLinkDisplayText('monero', '47R4NpvudmrLkLxaf4Uyiq56weFDZko1KeFrY5qUgnJ95X3D1YWYRVASAnMLBgpB5BeSViAVaxLXuFDuup15j3f45NC2WUp') |
||||
).toBe('47R4Npvudm... (Monero)') |
||||
}) |
||||
|
||||
it('formats short paypal handle without ellipsis', () => { |
||||
expect(formatPaytoLinkDisplayText('paypal', 'derjuergen')).toBe('derjuergen (PayPal)') |
||||
}) |
||||
}) |
||||
|
||||
describe('paytoLinkChildTextLooksLikeAuthority', () => { |
||||
it('detects full authority as link text', () => { |
||||
const auth = '47R4NpvudmrLkLxaf4Uy' |
||||
expect(paytoLinkChildTextLooksLikeAuthority(auth, auth, `payto://monero/${auth}`)).toBe(true) |
||||
}) |
||||
|
||||
it('keeps custom link text', () => { |
||||
expect(paytoLinkChildTextLooksLikeAuthority('Donate via PayPal', 'derjuergen', 'payto://paypal/derjuergen')).toBe( |
||||
false |
||||
) |
||||
}) |
||||
}) |
||||
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
import { Children, isValidElement, type ReactNode } from 'react' |
||||
import { getPaytoTypeInfo } from '@/lib/payto-registry' |
||||
|
||||
export const PAYTO_INLINE_DISPLAY_AUTHORITY_CHARS = 10 |
||||
|
||||
/** First N characters of a payto authority for inline feed/article display. */ |
||||
export function truncatePaytoAuthority( |
||||
authority: string, |
||||
maxLen = PAYTO_INLINE_DISPLAY_AUTHORITY_CHARS |
||||
): string { |
||||
const trimmed = authority.trim() |
||||
if (trimmed.length <= maxLen) return trimmed |
||||
return trimmed.slice(0, maxLen) |
||||
} |
||||
|
||||
/** |
||||
* Inline payto label for Markdown/AsciiDoc/notes: `47R4Npvudm... (Monero)`. |
||||
*/ |
||||
export function formatPaytoLinkDisplayText( |
||||
type: string, |
||||
authority: string, |
||||
options?: { label?: string; maxAuthorityChars?: number } |
||||
): string { |
||||
const info = getPaytoTypeInfo(type) |
||||
const label = (options?.label ?? info?.label ?? type).trim() |
||||
const maxLen = options?.maxAuthorityChars ?? PAYTO_INLINE_DISPLAY_AUTHORITY_CHARS |
||||
const trimmed = authority.trim() |
||||
const short = truncatePaytoAuthority(trimmed, maxLen) |
||||
const suffix = trimmed.length > short.length ? '...' : '' |
||||
return `${short}${suffix} (${label})` |
||||
} |
||||
|
||||
/** True when link text is just the raw address/URI (replace with compact display). */ |
||||
export function paytoLinkChildTextLooksLikeAuthority( |
||||
childText: string, |
||||
authority: string, |
||||
raw: string |
||||
): boolean { |
||||
const t = childText.trim() |
||||
if (!t) return true |
||||
const auth = authority.trim() |
||||
const uri = raw.trim() |
||||
if (t === auth || t === uri) return true |
||||
if (/^payto:\/\//i.test(t)) return true |
||||
if (auth.length > PAYTO_INLINE_DISPLAY_AUTHORITY_CHARS) { |
||||
if (t === auth || t.startsWith(auth.slice(0, PAYTO_INLINE_DISPLAY_AUTHORITY_CHARS))) { |
||||
return true |
||||
} |
||||
} |
||||
return false |
||||
} |
||||
|
||||
/** Plain text from PaytoLink child nodes (for detecting raw-address link labels). */ |
||||
export function flattenPaytoLinkChildText(children: ReactNode): string { |
||||
const parts: string[] = [] |
||||
Children.forEach(children, (child) => { |
||||
if (child == null || typeof child === 'boolean') return |
||||
if (typeof child === 'string' || typeof child === 'number') { |
||||
parts.push(String(child)) |
||||
return |
||||
} |
||||
if (isValidElement(child) && child.props.children != null) { |
||||
parts.push(flattenPaytoLinkChildText(child.props.children)) |
||||
} |
||||
}) |
||||
return parts.join('') |
||||
} |
||||
Loading…
Reference in new issue