Release 202505261604
This commit is contained in:
@@ -160,6 +160,7 @@ const splashText = showSplashText ? sample(splashTexts) : null
|
||||
<a
|
||||
href={makeUnimpersonateUrl(Astro.url)}
|
||||
data-astro-reload
|
||||
data-astro-prefetch="tap"
|
||||
class="xs:px-3 2xs:px-2 last:xs:-mr-3 last:2xs:-mr-2 flex h-full items-center px-1 text-sm text-stone-100 transition-colors last:-mr-1 hover:text-stone-200"
|
||||
transition:name="header-unimpersonate-link"
|
||||
aria-label="Unimpersonate"
|
||||
|
||||
@@ -8,7 +8,7 @@ type ContactMethodInfo<T extends string | null | undefined = string> = {
|
||||
label: string
|
||||
/** Notice that the first capture group is then used to format the value */
|
||||
matcher: RegExp
|
||||
formatter: (value: string) => string | null
|
||||
formatter: (match: RegExpMatchArray) => string | null
|
||||
icon: string
|
||||
}
|
||||
|
||||
@@ -24,82 +24,96 @@ export const {
|
||||
label: type ? transformCase(type, 'title') : String(type),
|
||||
icon: 'ri:shield-fill',
|
||||
matcher: /(.*)/,
|
||||
formatter: (value) => value,
|
||||
formatter: ([, value]) => value ?? String(value),
|
||||
}),
|
||||
[
|
||||
{
|
||||
type: 'email',
|
||||
label: 'Email',
|
||||
matcher: /mailto:(.+)/,
|
||||
formatter: (value) => value,
|
||||
formatter: ([, value]) => value ?? 'Email',
|
||||
icon: 'ri:mail-line',
|
||||
},
|
||||
{
|
||||
type: 'telephone',
|
||||
label: 'Telephone',
|
||||
matcher: /tel:(.+)/,
|
||||
formatter: (value) => {
|
||||
return parsePhoneNumberWithError(value).formatInternational()
|
||||
formatter: ([, value]) => {
|
||||
return value ? parsePhoneNumberWithError(value).formatInternational() : 'Telephone'
|
||||
},
|
||||
icon: 'ri:phone-line',
|
||||
},
|
||||
{
|
||||
type: 'whatsapp',
|
||||
label: 'WhatsApp',
|
||||
matcher: /https?:\/\/(?:www\.)?wa\.me\/(.+)/,
|
||||
formatter: (value) => {
|
||||
return parsePhoneNumberWithError(value).formatInternational()
|
||||
matcher: /^https?:\/\/(?:www\.)?wa\.me\/(.+)/,
|
||||
formatter: ([, value]) => {
|
||||
return value ? parsePhoneNumberWithError(value).formatInternational() : 'WhatsApp'
|
||||
},
|
||||
icon: 'ri:whatsapp-line',
|
||||
},
|
||||
{
|
||||
type: 'telegram',
|
||||
label: 'Telegram',
|
||||
matcher: /https?:\/\/(?:www\.)?t\.me\/(.+)/,
|
||||
formatter: (value) => `t.me/${value}`,
|
||||
matcher: /^https?:\/\/(?:www\.)?t\.me\/(.+)/,
|
||||
formatter: ([, value]) => (value ? `t.me/${value}` : 'Telegram'),
|
||||
icon: 'ri:telegram-line',
|
||||
},
|
||||
{
|
||||
type: 'linkedin',
|
||||
label: 'LinkedIn',
|
||||
matcher: /https?:\/\/(?:www\.)?linkedin\.com\/(?:in|company)\/(.+)/,
|
||||
formatter: (value) => `in/${value}`,
|
||||
matcher: /^https?:\/\/(?:www\.)?linkedin\.com\/(?:in|company)\/(.+)/,
|
||||
formatter: ([, value]) => (value ? `in/${value}` : 'LinkedIn'),
|
||||
icon: 'ri:linkedin-box-line',
|
||||
},
|
||||
{
|
||||
type: 'x',
|
||||
label: 'X',
|
||||
matcher: /https?:\/\/(?:www\.)?x\.com\/(.+)/,
|
||||
formatter: (value) => `@${value}`,
|
||||
matcher: /^https?:\/\/(?:www\.)?x\.com\/(.+)/,
|
||||
formatter: ([, value]) => (value ? `@${value}` : 'X'),
|
||||
icon: 'ri:twitter-x-line',
|
||||
},
|
||||
{
|
||||
type: 'instagram',
|
||||
label: 'Instagram',
|
||||
matcher: /https?:\/\/(?:www\.)?instagram\.com\/(.+)/,
|
||||
formatter: (value) => `@${value}`,
|
||||
matcher: /^https?:\/\/(?:www\.)?instagram\.com\/(.+)/,
|
||||
formatter: ([, value]) => (value ? `@${value}` : 'Instagram'),
|
||||
icon: 'ri:instagram-line',
|
||||
},
|
||||
{
|
||||
type: 'matrix',
|
||||
label: 'Matrix',
|
||||
matcher: /https?:\/\/(?:www\.)?matrix\.to\/#\/(.+)/,
|
||||
formatter: (value) => value,
|
||||
matcher: /^https?:\/\/(?:www\.)?matrix\.to\/#\/(.+)/,
|
||||
formatter: ([, value]) => (value ? `#${value}` : 'Matrix'),
|
||||
icon: 'ri:hashtag',
|
||||
},
|
||||
{
|
||||
type: 'bitcointalk',
|
||||
label: 'BitcoinTalk',
|
||||
matcher: /https?:\/\/(?:www\.)?bitcointalk\.org/,
|
||||
matcher: /^https?:\/\/(?:www\.)?bitcointalk\.org/,
|
||||
formatter: () => 'BitcoinTalk',
|
||||
icon: 'ri:btc-line',
|
||||
},
|
||||
// Website must go last because it's a catch-all
|
||||
{
|
||||
type: 'simplex',
|
||||
label: 'SimpleX Chat',
|
||||
matcher: /^https?:\/\/(?:www\.)?(simplex\.chat)\//,
|
||||
formatter: () => 'SimpleX Chat',
|
||||
icon: 'simplex',
|
||||
},
|
||||
{
|
||||
type: 'nostr',
|
||||
label: 'Nostr',
|
||||
matcher: /\b(npub1[a-zA-Z0-9]{58})\b/,
|
||||
formatter: () => 'Nostr',
|
||||
icon: 'nostr',
|
||||
},
|
||||
{
|
||||
// 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,
|
||||
matcher: /^https?:\/\/(?:www\.)?((?:[a-zA-Z0-9-]+\.)+[a-zA-Z]+)/,
|
||||
formatter: ([, value]) => value ?? 'Website',
|
||||
icon: 'ri:global-line',
|
||||
},
|
||||
] as const satisfies ContactMethodInfo[]
|
||||
@@ -107,10 +121,10 @@ export const {
|
||||
|
||||
export function formatContactMethod(url: string) {
|
||||
for (const contactMethod of contactMethods) {
|
||||
const captureGroup = url.match(contactMethod.matcher)?.[1]
|
||||
if (!captureGroup) continue
|
||||
const match = url.match(contactMethod.matcher)
|
||||
if (!match) continue
|
||||
|
||||
const formattedValue = contactMethod.formatter(captureGroup)
|
||||
const formattedValue = contactMethod.formatter(match)
|
||||
if (!formattedValue) continue
|
||||
|
||||
return {
|
||||
|
||||
@@ -67,6 +67,15 @@ Once submitted, you get a unique tracking page where you can monitor its status
|
||||
|
||||
All new listings begin as **unlisted** — they're only accessible via direct link and won't appear in search results. After a brief admin review to confirm the request isn't spam or inappropriate, the listing will be marked as **Community Contributed**.
|
||||
|
||||
#### Requirements
|
||||
|
||||
To list a new service, it must fulfill these requirements:
|
||||
|
||||
- Publicly available website explaining what the service is about
|
||||
- Terms of service or FAQ document
|
||||
|
||||
For example, just a Telegram link is not a valid service.
|
||||
|
||||
### Suggestion Review Process
|
||||
|
||||
#### First Review
|
||||
|
||||
@@ -50,6 +50,7 @@ if (reasonType === 'admin-required' && Astro.locals.user?.admin) {
|
||||
<a
|
||||
href={makeLoginUrl(Astro.url, { redirect, logout: true, message: reason })}
|
||||
data-astro-reload
|
||||
data-astro-prefetch="tap"
|
||||
class="focus-visible:outline-primary group flex items-center gap-2 px-3.5 py-2.5 text-white"
|
||||
>
|
||||
<Icon
|
||||
@@ -62,6 +63,7 @@ if (reasonType === 'admin-required' && Astro.locals.user?.admin) {
|
||||
Astro.locals.actualUser && (
|
||||
<a
|
||||
href={makeUnimpersonateUrl(Astro.url, { redirect })}
|
||||
data-astro-prefetch="tap"
|
||||
class="focus-visible:outline-primary group flex items-center gap-2 px-3.5 py-2.5 text-white"
|
||||
>
|
||||
<Icon
|
||||
|
||||
Reference in New Issue
Block a user