Release 202507080931
This commit is contained in:
@@ -43,6 +43,7 @@ const Tag = announcement.link ? 'a' : 'div'
|
|||||||
'group xs:px-6 2xs:px-4 relative isolate z-50 flex items-center justify-center gap-x-2 overflow-hidden border-b border-zinc-800 bg-black px-2 py-2 focus-visible:outline-none sm:gap-x-6 sm:px-3.5',
|
'group xs:px-6 2xs:px-4 relative isolate z-50 flex items-center justify-center gap-x-2 overflow-hidden border-b border-zinc-800 bg-black px-2 py-2 focus-visible:outline-none sm:gap-x-6 sm:px-3.5',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
aria-label="Announcement banner"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ const splashText = showSplashText ? sample(splashTexts) : null
|
|||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
transition:name="header-container"
|
transition:name="header-container"
|
||||||
|
aria-label="Header"
|
||||||
>
|
>
|
||||||
<nav class={cn('container mx-auto flex h-full w-full items-stretch justify-between px-4', classNames?.nav)}>
|
<nav class={cn('container mx-auto flex h-full w-full items-stretch justify-between px-4', classNames?.nav)}>
|
||||||
<div class="@container -ml-4 flex max-w-[192px] grow-99999 items-center">
|
<div class="@container -ml-4 flex max-w-[192px] grow-99999 items-center">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import Tooltip from './Tooltip.astro'
|
|||||||
import type { Prisma } from '@prisma/client'
|
import type { Prisma } from '@prisma/client'
|
||||||
import type { HTMLAttributes } from 'astro/types'
|
import type { HTMLAttributes } from 'astro/types'
|
||||||
|
|
||||||
type Props = HTMLAttributes<'a'> & {
|
type Props = HTMLAttributes<'article'> & {
|
||||||
inlineIcons?: boolean
|
inlineIcons?: boolean
|
||||||
withoutLink?: boolean
|
withoutLink?: boolean
|
||||||
service: Prisma.ServiceGetPayload<{
|
service: Prisma.ServiceGetPayload<{
|
||||||
@@ -57,7 +57,7 @@ const {
|
|||||||
},
|
},
|
||||||
class: className,
|
class: className,
|
||||||
withoutLink = false,
|
withoutLink = false,
|
||||||
...aProps
|
...htmlProps
|
||||||
} = Astro.props
|
} = Astro.props
|
||||||
|
|
||||||
const statusIcon = {
|
const statusIcon = {
|
||||||
@@ -70,127 +70,129 @@ const Element = withoutLink ? 'div' : 'a'
|
|||||||
const overallScoreInfo = makeOverallScoreInfo(overallScore)
|
const overallScoreInfo = makeOverallScoreInfo(overallScore)
|
||||||
---
|
---
|
||||||
|
|
||||||
<Element
|
<article {...htmlProps}>
|
||||||
href={Element === 'a' ? `/service/${slug}` : undefined}
|
<Element
|
||||||
{...aProps}
|
href={Element === 'a' ? `/service/${slug}` : undefined}
|
||||||
class={cn(
|
aria-label={Element === 'a' ? name : undefined}
|
||||||
'border-night-600 group/card bg-night-800 flex flex-col gap-(--gap) rounded-xl border p-(--gap) [--gap:calc(var(--spacing)*3)]',
|
class={cn(
|
||||||
(serviceVisibility === 'ARCHIVED' || verificationStatus === 'VERIFICATION_FAILED') &&
|
'border-night-600 group/card bg-night-800 flex flex-col gap-(--gap) rounded-xl border p-(--gap) [--gap:calc(var(--spacing)*3)]',
|
||||||
'opacity-75 transition-opacity hover:opacity-100 focus-visible:opacity-100',
|
(serviceVisibility === 'ARCHIVED' || verificationStatus === 'VERIFICATION_FAILED') &&
|
||||||
className
|
'opacity-75 transition-opacity hover:opacity-100 focus-visible:opacity-100',
|
||||||
)}
|
className
|
||||||
>
|
)}
|
||||||
<!-- Header with Icon and Title -->
|
>
|
||||||
<div class="flex items-center gap-(--gap)">
|
<!-- Header with Icon and Title -->
|
||||||
<MyPicture
|
<div class="flex items-center gap-(--gap)">
|
||||||
src={imageUrl}
|
<MyPicture
|
||||||
fallback="service"
|
src={imageUrl}
|
||||||
alt={name || 'Service logo'}
|
fallback="service"
|
||||||
class={cn(
|
alt="Logo"
|
||||||
'size-12 shrink-0 rounded-sm object-contain text-white',
|
class={cn(
|
||||||
(serviceVisibility === 'ARCHIVED' || verificationStatus === 'VERIFICATION_FAILED') &&
|
'size-12 shrink-0 rounded-sm object-contain text-white',
|
||||||
'grayscale-67 transition-all group-hover/card:grayscale-0 group-focus-visible/card:grayscale-0'
|
(serviceVisibility === 'ARCHIVED' || verificationStatus === 'VERIFICATION_FAILED') &&
|
||||||
)}
|
'grayscale-67 transition-all group-hover/card:grayscale-0 group-focus-visible/card:grayscale-0'
|
||||||
width={48}
|
)}
|
||||||
height={48}
|
width={48}
|
||||||
/>
|
height={48}
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="flex min-w-0 flex-1 flex-col justify-center self-stretch">
|
<div class="flex min-w-0 flex-1 flex-col justify-center self-stretch">
|
||||||
<h3 class="font-title text-lg leading-none font-medium tracking-wide text-white">
|
<h1 class="font-title text-lg leading-none font-medium tracking-wide text-white">
|
||||||
{name}{
|
{name}{
|
||||||
statusIcon && (
|
statusIcon && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
text={statusIcon.label}
|
text={statusIcon.label}
|
||||||
position="right"
|
position="right"
|
||||||
class="-my-2 shrink-0 whitespace-nowrap"
|
class="-my-2 shrink-0 whitespace-nowrap"
|
||||||
enabled={verificationStatus !== 'VERIFICATION_FAILED'}
|
enabled={verificationStatus !== 'VERIFICATION_FAILED'}
|
||||||
>
|
>
|
||||||
{[
|
{[
|
||||||
|
<Icon
|
||||||
|
is:inline={inlineIcons}
|
||||||
|
name={statusIcon.icon}
|
||||||
|
class={cn(
|
||||||
|
'inline-block size-6 shrink-0 rounded-lg p-1 align-[-0.37em]',
|
||||||
|
verificationStatus === 'VERIFICATION_FAILED' && 'pr-0',
|
||||||
|
statusIcon.classNames.icon
|
||||||
|
)}
|
||||||
|
/>,
|
||||||
|
verificationStatus === 'VERIFICATION_FAILED' && (
|
||||||
|
<span class="text-sm font-bold text-red-500">SCAM</span>
|
||||||
|
),
|
||||||
|
]}
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}{
|
||||||
|
serviceVisibility === 'ARCHIVED' && (
|
||||||
|
<Tooltip
|
||||||
|
text={serviceVisibilitiesById.ARCHIVED.label}
|
||||||
|
position="right"
|
||||||
|
class="-my-2 shrink-0 whitespace-nowrap"
|
||||||
|
>
|
||||||
<Icon
|
<Icon
|
||||||
is:inline={inlineIcons}
|
is:inline={inlineIcons}
|
||||||
name={statusIcon.icon}
|
name={serviceVisibilitiesById.ARCHIVED.icon}
|
||||||
class={cn(
|
class={cn(
|
||||||
'inline-block size-6 shrink-0 rounded-lg p-1 align-[-0.37em]',
|
'inline-block size-6 shrink-0 rounded-lg p-1 align-[-0.37em]',
|
||||||
verificationStatus === 'VERIFICATION_FAILED' && 'pr-0',
|
serviceVisibilitiesById.ARCHIVED.iconClass
|
||||||
statusIcon.classNames.icon
|
|
||||||
)}
|
)}
|
||||||
/>,
|
/>
|
||||||
verificationStatus === 'VERIFICATION_FAILED' && (
|
</Tooltip>
|
||||||
<span class="text-sm font-bold text-red-500">SCAM</span>
|
)
|
||||||
),
|
}
|
||||||
]}
|
</h1>
|
||||||
</Tooltip>
|
<div class="max-h-2 flex-1" aria-hidden="true"></div>
|
||||||
)
|
<div class="flex items-center gap-4 overflow-hidden mask-r-from-[calc(100%-var(--spacing)*4)]">
|
||||||
}{
|
{
|
||||||
serviceVisibility === 'ARCHIVED' && (
|
categories.map((category) => (
|
||||||
<Tooltip
|
<span class="text-day-300 inline-flex shrink-0 items-center gap-1 text-sm leading-none">
|
||||||
text={serviceVisibilitiesById.ARCHIVED.label}
|
<Icon name={category.icon} class="size-4" is:inline={inlineIcons} />
|
||||||
position="right"
|
<span>{category.name}</span>
|
||||||
class="-my-2 shrink-0 whitespace-nowrap"
|
</span>
|
||||||
>
|
))
|
||||||
<Icon
|
}
|
||||||
is:inline={inlineIcons}
|
</div>
|
||||||
name={serviceVisibilitiesById.ARCHIVED.icon}
|
</div>
|
||||||
class={cn(
|
</div>
|
||||||
'inline-block size-6 shrink-0 rounded-lg p-1 align-[-0.37em]',
|
|
||||||
serviceVisibilitiesById.ARCHIVED.iconClass
|
<div class="flex-1">
|
||||||
)}
|
<p class="text-day-400 line-clamp-3 text-sm leading-tight">
|
||||||
/>
|
{description}
|
||||||
</Tooltip>
|
</p>
|
||||||
)
|
</div>
|
||||||
}
|
|
||||||
</h3>
|
<div class="flex items-center justify-start">
|
||||||
<div class="max-h-2 flex-1"></div>
|
<Tooltip
|
||||||
<div class="flex items-center gap-4 overflow-hidden mask-r-from-[calc(100%-var(--spacing)*4)]">
|
class={cn(
|
||||||
|
'inline-flex size-6 items-center justify-center rounded-sm text-lg font-bold',
|
||||||
|
overallScoreInfo.classNameBg
|
||||||
|
)}
|
||||||
|
text={`${Math.round(privacyScore).toLocaleString()}% Privacy | ${Math.round(trustScore).toLocaleString()}% Trust`}
|
||||||
|
>
|
||||||
|
{overallScoreInfo.formattedScore}
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<span class="text-day-300 ml-3 text-sm font-bold whitespace-nowrap">
|
||||||
|
KYC {kycLevel.toLocaleString()}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="-m-1 ml-auto flex">
|
||||||
{
|
{
|
||||||
categories.map((category) => (
|
currencies.map((currency) => {
|
||||||
<span class="text-day-300 inline-flex shrink-0 items-center gap-1 text-sm leading-none">
|
const isAccepted = acceptedCurrencies.includes(currency.id)
|
||||||
<Icon name={category.icon} class="size-4" is:inline={inlineIcons} />
|
|
||||||
<span>{category.name}</span>
|
return (
|
||||||
</span>
|
<Tooltip text={currency.name}>
|
||||||
))
|
<Icon
|
||||||
|
is:inline={inlineIcons}
|
||||||
|
name={currency.icon}
|
||||||
|
class={cn('text-day-600 box-content size-4 p-1', { 'text-white': isAccepted })}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Element>
|
||||||
|
</article>
|
||||||
<div class="flex-1">
|
|
||||||
<p class="text-day-400 line-clamp-3 text-sm leading-tight">
|
|
||||||
{description}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center justify-start">
|
|
||||||
<Tooltip
|
|
||||||
class={cn(
|
|
||||||
'inline-flex size-6 items-center justify-center rounded-sm text-lg font-bold',
|
|
||||||
overallScoreInfo.classNameBg
|
|
||||||
)}
|
|
||||||
text={`${Math.round(privacyScore).toLocaleString()}% Privacy | ${Math.round(trustScore).toLocaleString()}% Trust`}
|
|
||||||
>
|
|
||||||
{overallScoreInfo.formattedScore}
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<span class="text-day-300 ml-3 text-sm font-bold whitespace-nowrap">
|
|
||||||
KYC {kycLevel.toLocaleString()}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div class="-m-1 ml-auto flex">
|
|
||||||
{
|
|
||||||
currencies.map((currency) => {
|
|
||||||
const isAccepted = acceptedCurrencies.includes(currency.id)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tooltip text={currency.name}>
|
|
||||||
<Icon
|
|
||||||
is:inline={inlineIcons}
|
|
||||||
name={currency.icon}
|
|
||||||
class={cn('text-day-600 box-content size-4 p-1', { 'text-white': isAccepted })}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Element>
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ const makeUrlWithoutFilter = (filter: string, value?: string) => {
|
|||||||
'bg-night-800 hover:bg-night-900 border-night-400 flex h-8 shrink-0 items-center gap-2 rounded-full border px-3 text-sm text-white',
|
'bg-night-800 hover:bg-night-900 border-night-400 flex h-8 shrink-0 items-center gap-2 rounded-full border px-3 text-sm text-white',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
aria-label={`Remove filter: ${text}`}
|
||||||
>
|
>
|
||||||
{icon && <Icon name={icon} class={cn('size-4', iconClass)} is:inline={inlineIcons} />}
|
{icon && <Icon name={icon} class={cn('size-4', iconClass)} is:inline={inlineIcons} />}
|
||||||
{text}
|
{text}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ const {
|
|||||||
'not-pointer-coarse:no-scrollbar -ml-4 flex shrink grow items-center gap-2 overflow-x-auto mask-r-from-[calc(100%-var(--spacing)*16)] pr-12 pl-4',
|
'not-pointer-coarse:no-scrollbar -ml-4 flex shrink grow items-center gap-2 overflow-x-auto mask-r-from-[calc(100%-var(--spacing)*16)] pr-12 pl-4',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
aria-label="Applied filters"
|
||||||
{...divProps}
|
{...divProps}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ const searchTitle = (() => {
|
|||||||
name="ri:loader-4-line"
|
name="ri:loader-4-line"
|
||||||
id="search-indicator"
|
id="search-indicator"
|
||||||
class="htmx-request:opacity-100 xs:-mx-1.5 -mx-1 inline-block size-4 animate-spin text-white opacity-0 transition-opacity duration-500 sm:-mx-3"
|
class="htmx-request:opacity-100 xs:-mx-1.5 -mx-1 inline-block size-4 animate-spin text-white opacity-0 transition-opacity duration-500 sm:-mx-3"
|
||||||
|
aria-hidden="true"
|
||||||
is:inline={inlineIcons}
|
is:inline={inlineIcons}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -346,24 +347,26 @@ const searchTitle = (() => {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div class="mt-6 grid grid-cols-1 gap-4 sm:gap-6 md:grid-cols-[repeat(auto-fill,minmax(calc(var(--spacing)*80),1fr))]">
|
<ol class="mt-6 grid grid-cols-1 gap-4 sm:gap-6 md:grid-cols-[repeat(auto-fill,minmax(calc(var(--spacing)*80),1fr))]">
|
||||||
{services.map((service, i) => (
|
{services.map((service, i) => (
|
||||||
<ServiceCard
|
<li>
|
||||||
inlineIcons={inlineIcons}
|
<ServiceCard
|
||||||
service={service}
|
inlineIcons={inlineIcons}
|
||||||
data-hx-search-results-card
|
service={service}
|
||||||
{...(i === services.length - 1 && currentPage < totalPages
|
data-hx-search-results-card
|
||||||
? {
|
{...(i === services.length - 1 && currentPage < totalPages
|
||||||
'hx-get': createPageUrl(currentPage + 1, Astro.url, { 'sort-seed': sortSeed }),
|
? {
|
||||||
'hx-trigger': 'revealed',
|
'hx-get': createPageUrl(currentPage + 1, Astro.url, { 'sort-seed': sortSeed }),
|
||||||
'hx-swap': 'afterend',
|
'hx-trigger': 'revealed',
|
||||||
'hx-select': '[data-hx-search-results-card]',
|
'hx-swap': 'afterend',
|
||||||
'hx-indicator': '#infinite-scroll-indicator',
|
'hx-select': '[data-hx-search-results-card]',
|
||||||
}
|
'hx-indicator': '#infinite-scroll-indicator',
|
||||||
: {})}
|
}
|
||||||
/>
|
: {})}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
))}
|
))}
|
||||||
</div>
|
</ol>
|
||||||
|
|
||||||
<div class="no-js:hidden mt-8 flex justify-center" id="infinite-scroll-indicator">
|
<div class="no-js:hidden mt-8 flex justify-center" id="infinite-scroll-indicator">
|
||||||
<div class="htmx-request:opacity-100 flex items-center gap-2 opacity-0 transition-opacity duration-500">
|
<div class="htmx-request:opacity-100 flex items-center gap-2 opacity-0 transition-opacity duration-500">
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const {
|
|||||||
enabled && (
|
enabled && (
|
||||||
<span
|
<span
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
|
aria-hidden="true"
|
||||||
class={cn(
|
class={cn(
|
||||||
'pointer-events-none hidden select-none group-hover/tooltip:flex',
|
'pointer-events-none hidden select-none group-hover/tooltip:flex',
|
||||||
'ease-out-cubic scale-75 opacity-0 transition-all transition-discrete duration-100 group-hover/tooltip:scale-100 group-hover/tooltip:opacity-100 starting:group-hover/tooltip:scale-75 starting:group-hover/tooltip:opacity-0',
|
'ease-out-cubic scale-75 opacity-0 transition-all transition-discrete duration-100 group-hover/tooltip:scale-100 group-hover/tooltip:opacity-100 starting:group-hover/tooltip:scale-75 starting:group-hover/tooltip:opacity-0',
|
||||||
|
|||||||
@@ -492,6 +492,7 @@ const showFiltersId = 'show-filters'
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="bg-night-700 fixed top-0 left-0 z-50 hidden h-dvh w-dvw shrink-0 translate-y-full overflow-y-auto overscroll-contain border-t border-green-500/30 px-8 pt-4 transition-transform peer-checked:translate-y-0 max-sm:peer-checked:block sm:relative sm:z-auto sm:block sm:h-auto sm:w-64 sm:translate-y-0 sm:overflow-visible sm:border-none sm:bg-none sm:p-0"
|
class="bg-night-700 fixed top-0 left-0 z-50 hidden h-dvh w-dvw shrink-0 translate-y-full overflow-y-auto overscroll-contain border-t border-green-500/30 px-8 pt-4 transition-transform peer-checked:translate-y-0 max-sm:peer-checked:block sm:relative sm:z-auto sm:block sm:h-auto sm:w-64 sm:translate-y-0 sm:overflow-visible sm:border-none sm:bg-none sm:p-0"
|
||||||
|
aria-label="Search filters"
|
||||||
>
|
>
|
||||||
<ServicesFilters
|
<ServicesFilters
|
||||||
searchResultsId={searchResultsId}
|
searchResultsId={searchResultsId}
|
||||||
@@ -519,6 +520,7 @@ const showFiltersId = 'show-filters'
|
|||||||
filtersOptions={filtersOptions}
|
filtersOptions={filtersOptions}
|
||||||
attributes={attributes}
|
attributes={attributes}
|
||||||
attributeOptions={attributeOptions}
|
attributeOptions={attributeOptions}
|
||||||
|
aria-label="Search results"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user