124 lines
3.3 KiB
TypeScript
124 lines
3.3 KiB
TypeScript
import { parsePhoneNumberWithError } from 'libphonenumber-js'
|
|
|
|
import { makeHelpersForOptions } from '../lib/makeHelpersForOptions'
|
|
import { transformCase } from '../lib/strings'
|
|
|
|
type ContactMethodInfo<T extends string | null | undefined = string> = {
|
|
type: T
|
|
label: string
|
|
/** Notice that the first capture group is then used to format the value */
|
|
matcher: RegExp
|
|
formatter: (value: string) => string | null
|
|
icon: string
|
|
}
|
|
|
|
export const {
|
|
dataArray: contactMethods,
|
|
dataObject: contactMethodsById,
|
|
/** Use {@link formatContactMethod} instead */
|
|
getFn: getContactMethodInfo,
|
|
} = makeHelpersForOptions(
|
|
'type',
|
|
(type): ContactMethodInfo<typeof type> => ({
|
|
type,
|
|
label: type ? transformCase(type, 'title') : String(type),
|
|
icon: 'ri:shield-fill',
|
|
matcher: /(.*)/,
|
|
formatter: (value) => value,
|
|
}),
|
|
[
|
|
{
|
|
type: 'email',
|
|
label: 'Email',
|
|
matcher: /mailto:(.+)/,
|
|
formatter: (value) => value,
|
|
icon: 'ri:mail-line',
|
|
},
|
|
{
|
|
type: 'telephone',
|
|
label: 'Telephone',
|
|
matcher: /tel:(.+)/,
|
|
formatter: (value) => {
|
|
return parsePhoneNumberWithError(value).formatInternational()
|
|
},
|
|
icon: 'ri:phone-line',
|
|
},
|
|
{
|
|
type: 'whatsapp',
|
|
label: 'WhatsApp',
|
|
matcher: /https?:\/\/(?:www\.)?wa\.me\/(.+)/,
|
|
formatter: (value) => {
|
|
return parsePhoneNumberWithError(value).formatInternational()
|
|
},
|
|
icon: 'ri:whatsapp-line',
|
|
},
|
|
{
|
|
type: 'telegram',
|
|
label: 'Telegram',
|
|
matcher: /https?:\/\/(?:www\.)?t\.me\/(.+)/,
|
|
formatter: (value) => `t.me/${value}`,
|
|
icon: 'ri:telegram-line',
|
|
},
|
|
{
|
|
type: 'linkedin',
|
|
label: 'LinkedIn',
|
|
matcher: /https?:\/\/(?:www\.)?linkedin\.com\/(?:in|company)\/(.+)/,
|
|
formatter: (value) => `in/${value}`,
|
|
icon: 'ri:linkedin-box-line',
|
|
},
|
|
{
|
|
type: 'x',
|
|
label: 'X',
|
|
matcher: /https?:\/\/(?:www\.)?x\.com\/(.+)/,
|
|
formatter: (value) => `@${value}`,
|
|
icon: 'ri:twitter-x-line',
|
|
},
|
|
{
|
|
type: 'instagram',
|
|
label: 'Instagram',
|
|
matcher: /https?:\/\/(?:www\.)?instagram\.com\/(.+)/,
|
|
formatter: (value) => `@${value}`,
|
|
icon: 'ri:instagram-line',
|
|
},
|
|
{
|
|
type: 'matrix',
|
|
label: 'Matrix',
|
|
matcher: /https?:\/\/(?:www\.)?matrix\.to\/#\/(.+)/,
|
|
formatter: (value) => value,
|
|
icon: 'ri:hashtag',
|
|
},
|
|
{
|
|
type: 'bitcointalk',
|
|
label: 'BitcoinTalk',
|
|
matcher: /https?:\/\/(?:www\.)?bitcointalk\.org/,
|
|
formatter: () => 'BitcoinTalk',
|
|
icon: 'ri:btc-line',
|
|
},
|
|
// Website must go last because it's a catch-all
|
|
{
|
|
type: 'website',
|
|
label: 'Website',
|
|
matcher: /https?:\/\/(?:www\.)?((?:[a-zA-Z0-9-]+\.)+[a-zA-Z]+)/,
|
|
formatter: (value) => value,
|
|
icon: 'ri:global-line',
|
|
},
|
|
] as const satisfies ContactMethodInfo[]
|
|
)
|
|
|
|
export function formatContactMethod(url: string) {
|
|
for (const contactMethod of contactMethods) {
|
|
const captureGroup = url.match(contactMethod.matcher)?.[1]
|
|
if (!captureGroup) continue
|
|
|
|
const formattedValue = contactMethod.formatter(captureGroup)
|
|
if (!formattedValue) continue
|
|
|
|
return {
|
|
...contactMethod,
|
|
formattedValue,
|
|
} as const
|
|
}
|
|
|
|
return { ...getContactMethodInfo('unknown'), formattedValue: url } as const
|
|
}
|