Release 2025-05-20-0D8p
This commit is contained in:
@@ -4,6 +4,7 @@ title: About
|
||||
author: KYCnot.me
|
||||
pubDate: 2025-05-15
|
||||
description: 'Learn how KYCnot.me website works and about our mission to protect privacy in cryptocurrency.'
|
||||
icon: 'ri:information-line'
|
||||
---
|
||||
|
||||
## What is this page?
|
||||
|
||||
@@ -24,7 +24,7 @@ const inputErrors = isInputError(result?.error) ? result.error.fields : {}
|
||||
<MiniLayout
|
||||
pageTitle={`Edit Profile - ${user.name}`}
|
||||
description="Edit your user profile"
|
||||
ogImage={{ template: 'generic', title: 'Edit Profile' }}
|
||||
ogImage={{ template: 'generic', title: 'Edit Profile', icon: 'ri:user-settings-line' }}
|
||||
layoutHeader={{
|
||||
icon: 'ri:edit-line',
|
||||
title: 'Edit profile',
|
||||
|
||||
@@ -25,7 +25,12 @@ const prettyToken = preGeneratedToken ? prettifyUserSecretToken(preGeneratedToke
|
||||
<MiniLayout
|
||||
pageTitle="Create Account"
|
||||
description="Create a new account"
|
||||
ogImage={{ template: 'generic', title: 'Create Account' }}
|
||||
ogImage={{
|
||||
template: 'generic',
|
||||
title: 'Create Account',
|
||||
description: 'Zero data, 100% anonymous',
|
||||
icon: 'ri:user-add-line',
|
||||
}}
|
||||
layoutHeader={{
|
||||
icon: 'ri:user-add-line',
|
||||
title: 'New account',
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import { actions } from 'astro:actions'
|
||||
import { Picture } from 'astro:assets'
|
||||
import { sortBy } from 'lodash-es'
|
||||
|
||||
import defaultServiceImage from '../../assets/fallback-service-image.jpg'
|
||||
import BadgeSmall from '../../components/BadgeSmall.astro'
|
||||
import Button from '../../components/Button.astro'
|
||||
import MyPicture from '../../components/MyPicture.astro'
|
||||
import TimeFormatted from '../../components/TimeFormatted.astro'
|
||||
import Tooltip from '../../components/Tooltip.astro'
|
||||
import { getKarmaTransactionActionInfo } from '../../constants/karmaTransactionActions'
|
||||
@@ -161,7 +160,12 @@ if (!user) return Astro.rewrite('/404')
|
||||
<BaseLayout
|
||||
pageTitle={`${user.name} - Account`}
|
||||
description="Manage your user profile"
|
||||
ogImage={{ template: 'generic', title: `${user.name} | Account` }}
|
||||
ogImage={{
|
||||
template: 'generic',
|
||||
title: `${user.name} | Account`,
|
||||
description: 'Manage your user profile',
|
||||
icon: 'ri:user-3-line',
|
||||
}}
|
||||
widthClassName="max-w-screen-md"
|
||||
className={{
|
||||
main: 'space-y-6',
|
||||
@@ -180,7 +184,13 @@ if (!user) return Astro.rewrite('/404')
|
||||
<header class="flex items-center gap-4">
|
||||
{
|
||||
user.picture ? (
|
||||
<img src={user.picture} alt="" class="ring-day-500/30 size-16 rounded-full ring-2" />
|
||||
<MyPicture
|
||||
src={user.picture}
|
||||
alt=""
|
||||
class="ring-day-500/30 size-16 rounded-full ring-2"
|
||||
width={64}
|
||||
height={64}
|
||||
/>
|
||||
) : (
|
||||
<div class="bg-day-500/10 ring-day-500/30 text-day-400 flex size-16 items-center justify-center rounded-full ring-2">
|
||||
<Icon name="ri:user-3-line" class="size-8" />
|
||||
@@ -460,8 +470,9 @@ if (!user) return Astro.rewrite('/404')
|
||||
href={`/service/${affiliation.service.slug}`}
|
||||
class="text-day-300 group flex min-w-32 items-center gap-2 text-sm"
|
||||
>
|
||||
<Picture
|
||||
src={affiliation.service.imageUrl ?? (defaultServiceImage as unknown as string)}
|
||||
<MyPicture
|
||||
src={affiliation.service.imageUrl}
|
||||
fallback="service"
|
||||
alt={affiliation.service.name}
|
||||
width={40}
|
||||
height={40}
|
||||
|
||||
@@ -30,7 +30,12 @@ const message = Astro.url.searchParams.get('message')
|
||||
<MiniLayout
|
||||
pageTitle="Login"
|
||||
description="Login to your account"
|
||||
ogImage={{ template: 'generic', title: 'Login' }}
|
||||
ogImage={{
|
||||
template: 'generic',
|
||||
title: 'Login',
|
||||
description: message ?? 'Enter your login key',
|
||||
icon: 'ri:login-box-line',
|
||||
}}
|
||||
layoutHeader={{
|
||||
icon: 'ri:user-line',
|
||||
title: 'Welcome back',
|
||||
|
||||
@@ -17,7 +17,12 @@ const prettyToken = result ? prettifyUserSecretToken(result.data.token) : null
|
||||
<MiniLayout
|
||||
pageTitle="Welcome"
|
||||
description="New account welcome page"
|
||||
ogImage={{ template: 'generic', title: 'Welcome' }}
|
||||
ogImage={{
|
||||
template: 'generic',
|
||||
title: 'Welcome',
|
||||
description: 'New account welcome page',
|
||||
icon: 'ri:key-2-line',
|
||||
}}
|
||||
layoutHeader={{
|
||||
icon: 'ri:key-2-line',
|
||||
title: 'Save your Login Key',
|
||||
|
||||
@@ -8,8 +8,8 @@ import {
|
||||
} from '@prisma/client'
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import { actions, isInputError } from 'astro:actions'
|
||||
import { Image } from 'astro:assets'
|
||||
|
||||
import MyPicture from '../../../../components/MyPicture.astro'
|
||||
import { serviceVisibilities } from '../../../../constants/serviceVisibility'
|
||||
import BaseLayout from '../../../../layouts/BaseLayout.astro'
|
||||
import { cn } from '../../../../lib/cn'
|
||||
@@ -334,7 +334,7 @@ const buttonSmallWarningClasses = cn(
|
||||
{
|
||||
service.imageUrl ? (
|
||||
<div class="mt-2 shrink-0">
|
||||
<Image
|
||||
<MyPicture
|
||||
src={service.imageUrl}
|
||||
alt="Current service image"
|
||||
width={100}
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
import { ServiceVisibility, VerificationStatus, type Prisma } from '@prisma/client'
|
||||
import { z } from 'astro/zod'
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import { Image } from 'astro:assets'
|
||||
|
||||
import defaultImage from '../../../assets/fallback-service-image.jpg'
|
||||
import MyPicture from '../../../components/MyPicture.astro'
|
||||
import SortArrowIcon from '../../../components/SortArrowIcon.astro'
|
||||
import { getKycLevelInfo } from '../../../constants/kycLevels'
|
||||
import { getVerificationStatusInfo } from '../../../constants/verificationStatus'
|
||||
@@ -343,23 +342,14 @@ const truncate = (text: string, length: number) => {
|
||||
<td class="px-4 py-3">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="h-10 w-10 flex-shrink-0">
|
||||
{service.imageUrl ? (
|
||||
<Image
|
||||
src={service.imageUrl}
|
||||
alt={service.name}
|
||||
width={40}
|
||||
height={40}
|
||||
class="h-10 w-10 rounded-md object-cover"
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
src={defaultImage}
|
||||
alt={service.name}
|
||||
width={40}
|
||||
height={40}
|
||||
class="h-10 w-10 rounded-md object-cover"
|
||||
/>
|
||||
)}
|
||||
<MyPicture
|
||||
src={service.imageUrl}
|
||||
fallback="service"
|
||||
alt={service.name}
|
||||
width={40}
|
||||
height={40}
|
||||
class="h-10 w-10 rounded-md object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="text-sm font-medium text-zinc-200">{service.name}</div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import { actions, isInputError } from 'astro:actions'
|
||||
import { Image } from 'astro:assets'
|
||||
|
||||
import BadgeSmall from '../../../components/BadgeSmall.astro'
|
||||
import Button from '../../../components/Button.astro'
|
||||
@@ -11,6 +10,7 @@ import InputSelect from '../../../components/InputSelect.astro'
|
||||
import InputSubmitButton from '../../../components/InputSubmitButton.astro'
|
||||
import InputText from '../../../components/InputText.astro'
|
||||
import InputTextArea from '../../../components/InputTextArea.astro'
|
||||
import MyPicture from '../../../components/MyPicture.astro'
|
||||
import TimeFormatted from '../../../components/TimeFormatted.astro'
|
||||
import { getServiceUserRoleInfo, serviceUserRoles } from '../../../constants/serviceUserRoles'
|
||||
import BaseLayout from '../../../layouts/BaseLayout.astro'
|
||||
@@ -123,7 +123,7 @@ if (!user) return Astro.rewrite('/404')
|
||||
<div class="mt-12">
|
||||
{
|
||||
!!user.picture && (
|
||||
<Image
|
||||
<MyPicture
|
||||
src={user.picture}
|
||||
alt=""
|
||||
width={80}
|
||||
@@ -218,7 +218,7 @@ if (!user) return Astro.rewrite('/404')
|
||||
value={user.picture}
|
||||
error={updateInputErrors.pictureFile}
|
||||
square
|
||||
description="Upload a square image for best results. Supported formats: JPG, PNG, WebP, AVIF, JXL. Max size: 5MB."
|
||||
description="Upload a square image for best results. Supported formats: JPG, PNG, WebP, AVIF. Max size: 5MB."
|
||||
/>
|
||||
|
||||
<InputCardGroup
|
||||
@@ -265,7 +265,7 @@ if (!user) return Astro.rewrite('/404')
|
||||
<div class="mb-1 flex items-center justify-between gap-4">
|
||||
<div class="flex items-center gap-1">
|
||||
{!!note.addedByUser?.picture && (
|
||||
<Image
|
||||
<MyPicture
|
||||
src={note.addedByUser.picture}
|
||||
alt=""
|
||||
width={12}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import { Markdown } from 'astro-remote'
|
||||
import { Picture } from 'astro:assets'
|
||||
import { z } from 'astro:content'
|
||||
import { orderBy } from 'lodash-es'
|
||||
|
||||
import BadgeStandard from '../components/BadgeStandard.astro'
|
||||
import { makeOverallScoreInfo } from '../components/ScoreSquare.astro'
|
||||
import MyPicture from '../components/MyPicture.astro'
|
||||
import SortArrowIcon from '../components/SortArrowIcon.astro'
|
||||
import { getAttributeCategoryInfo } from '../constants/attributeCategories'
|
||||
import { getAttributeTypeInfo } from '../constants/attributeTypes'
|
||||
@@ -15,6 +14,7 @@ import BaseLayout from '../layouts/BaseLayout.astro'
|
||||
import { sortAttributes } from '../lib/attributes'
|
||||
import { cn } from '../lib/cn'
|
||||
import { formatNumber } from '../lib/numbers'
|
||||
import { makeOverallScoreInfo } from '../lib/overallScore'
|
||||
import { zodParseQueryParamsStoringErrors } from '../lib/parseUrlFilters'
|
||||
import { prisma } from '../lib/prisma'
|
||||
|
||||
@@ -102,8 +102,13 @@ const makeSortUrl = (slug: NonNullable<(typeof filters)['sort-by']>) => {
|
||||
|
||||
<BaseLayout
|
||||
pageTitle="Attributes"
|
||||
description="Browse all available service attributes used to evaluate privacy and trust scores on KYCnot.me."
|
||||
ogImage={{ template: 'generic', title: 'All attributes' }}
|
||||
description="Browse all available service attributes used to evaluate privacy and trust scores."
|
||||
ogImage={{
|
||||
template: 'generic',
|
||||
title: 'All attributes',
|
||||
description: 'Browse all available service attributes',
|
||||
icon: 'ri:list-radio',
|
||||
}}
|
||||
>
|
||||
<h1 class="font-title mb-2 text-center text-3xl font-bold text-white">Service attributes</h1>
|
||||
|
||||
@@ -202,12 +207,11 @@ const makeSortUrl = (slug: NonNullable<(typeof filters)['sort-by']>) => {
|
||||
class="flex items-center gap-2 rounded-md p-2 transition-colors hover:bg-zinc-800"
|
||||
>
|
||||
{service.imageUrl ? (
|
||||
<Picture
|
||||
<MyPicture
|
||||
src={service.imageUrl}
|
||||
alt={service.name}
|
||||
width={24}
|
||||
height={24}
|
||||
formats={['jxl', 'avif', 'webp']}
|
||||
class="size-6 shrink-0 rounded-xs object-contain"
|
||||
/>
|
||||
) : (
|
||||
@@ -349,12 +353,11 @@ const makeSortUrl = (slug: NonNullable<(typeof filters)['sort-by']>) => {
|
||||
class="flex items-center gap-2 rounded-md p-2 transition-colors hover:bg-zinc-800"
|
||||
>
|
||||
{service.imageUrl ? (
|
||||
<Picture
|
||||
<MyPicture
|
||||
src={service.imageUrl}
|
||||
alt={service.name}
|
||||
width={24}
|
||||
height={24}
|
||||
formats={['jxl', 'avif', 'webp']}
|
||||
class="size-6 shrink-0 rounded-xs object-contain"
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
import { z } from 'astro/zod'
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import { Picture } from 'astro:assets'
|
||||
import { orderBy } from 'lodash-es'
|
||||
|
||||
import Button from '../components/Button.astro'
|
||||
import FormatTimeInterval from '../components/FormatTimeInterval.astro'
|
||||
import MyPicture from '../components/MyPicture.astro'
|
||||
import TimeFormatted from '../components/TimeFormatted.astro'
|
||||
import {
|
||||
eventTypes,
|
||||
@@ -151,7 +151,12 @@ const createUrlWithoutFilter = (paramName: keyof typeof params) => {
|
||||
description="Discover important events, updates, and news about KYC-free services in chronological order."
|
||||
widthClassName="max-w-screen-lg"
|
||||
className={{ main: 'sm:flex sm:items-start sm:gap-6' }}
|
||||
ogImage={{ template: 'generic', title: 'Events' }}
|
||||
ogImage={{
|
||||
template: 'generic',
|
||||
title: 'Events',
|
||||
description: 'Discover important events, updates, and news about KYC-free services',
|
||||
icon: 'ri:calendar-event-line',
|
||||
}}
|
||||
htmx
|
||||
>
|
||||
<h1 class="font-title mb-6 block text-center text-2xl font-bold text-white sm:hidden">
|
||||
@@ -287,12 +292,11 @@ const createUrlWithoutFilter = (paramName: keyof typeof params) => {
|
||||
class="group flex h-8 items-center gap-2 rounded-full border border-green-500/30 bg-black/40 px-3 text-sm text-white"
|
||||
>
|
||||
{service?.imageUrl && (
|
||||
<Picture
|
||||
<MyPicture
|
||||
src={service.imageUrl}
|
||||
alt={service.name}
|
||||
width={16}
|
||||
height={16}
|
||||
formats={['jxl', 'avif', 'webp']}
|
||||
class="size-4 shrink-0 rounded-xs object-contain"
|
||||
/>
|
||||
)}
|
||||
@@ -385,12 +389,11 @@ const createUrlWithoutFilter = (paramName: keyof typeof params) => {
|
||||
class="-m-1.5 flex w-fit items-center rounded-md p-1.5 leading-none transition-colors hover:bg-zinc-800"
|
||||
>
|
||||
{event.service.imageUrl && (
|
||||
<Picture
|
||||
<MyPicture
|
||||
src={event.service.imageUrl}
|
||||
alt={event.service.name}
|
||||
width={16}
|
||||
height={16}
|
||||
formats={['jxl', 'avif', 'webp']}
|
||||
class="size-4 shrink-0 rounded-xs object-contain"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
layout: ../layouts/MarkdownLayout.astro
|
||||
title: How does karma work?
|
||||
description: "KYCnot.me has a user karma system, here's how it works"
|
||||
icon: 'ri:hearts-line'
|
||||
author: KYCnot.me
|
||||
pubDate: 2025-05-15
|
||||
---
|
||||
|
||||
@@ -183,7 +183,12 @@ const notifications = dbNotifications.map((notification) => ({
|
||||
pageTitle="Notifications"
|
||||
description="View your notifications and manage your notification preferences."
|
||||
widthClassName="max-w-screen-lg"
|
||||
ogImage={{ template: 'generic', title: 'Notifications' }}
|
||||
ogImage={{
|
||||
template: 'generic',
|
||||
title: 'Notifications',
|
||||
description: 'View and manage your notifications',
|
||||
icon: 'ri:notification-line',
|
||||
}}
|
||||
>
|
||||
<section class="mx-auto w-full">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
|
||||
@@ -1,19 +1,31 @@
|
||||
import { ogImageTemplates } from '../components/OgImage'
|
||||
import { urlParamsToObject } from '../lib/urls'
|
||||
import { ogImageTemplates, type OgImageAllTemplatesWithProps } from '../components/OgImage'
|
||||
|
||||
import type { APIRoute } from 'astro'
|
||||
import type { Misc } from 'ts-toolbelt'
|
||||
|
||||
export const GET: APIRoute = (context) => {
|
||||
const { template, ...props } = urlParamsToObject(context.url.searchParams)
|
||||
function toJSON<T extends Misc.JSON.Value>(data: string | null | undefined): T | undefined {
|
||||
if (!data) return undefined
|
||||
try {
|
||||
return JSON.parse(data) as T
|
||||
} catch (_error) {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
if (!template) return ogImageTemplates.default({}, context)
|
||||
export const GET: APIRoute = async (context) => {
|
||||
const { template, ...props } = toJSON<OgImageAllTemplatesWithProps>(
|
||||
context.url.searchParams.get('data')
|
||||
) ?? { template: 'default' }
|
||||
|
||||
if (!template as unknown) return ogImageTemplates.default({}, context)
|
||||
|
||||
if (!(template in ogImageTemplates)) {
|
||||
console.error(`Invalid template: "${template}"`)
|
||||
return ogImageTemplates.default({}, context)
|
||||
}
|
||||
|
||||
const response = ogImageTemplates[template as keyof typeof ogImageTemplates](props, context)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
|
||||
const response = await ogImageTemplates[template](props as any, context)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!response) {
|
||||
console.error(`Cannot generate image for template: ${template} and props: ${JSON.stringify(props)}`)
|
||||
|
||||
@@ -86,7 +86,12 @@ const statusInfo = getServiceSuggestionStatusInfo(serviceSuggestion.status)
|
||||
<BaseLayout
|
||||
pageTitle={`${serviceSuggestion.service.name} | Service suggestion`}
|
||||
description="View your service suggestion"
|
||||
ogImage={{ template: 'generic', title: serviceSuggestion.service.name }}
|
||||
ogImage={{
|
||||
template: 'generic',
|
||||
title: 'My service suggestions',
|
||||
description: 'View and manage your service suggestion',
|
||||
icon: 'ri:service-line',
|
||||
}}
|
||||
widthClassName="max-w-screen-md"
|
||||
htmx
|
||||
breadcrumbs={[
|
||||
|
||||
@@ -65,7 +65,12 @@ if (!service) return Astro.rewrite('/404')
|
||||
<BaseLayout
|
||||
pageTitle="Edit service"
|
||||
description="Suggest an edit to service"
|
||||
ogImage={{ template: 'generic', title: 'Edit service' }}
|
||||
ogImage={{
|
||||
template: 'generic',
|
||||
title: 'Edit service',
|
||||
description: 'Suggest an edit to service',
|
||||
icon: 'ri:edit-line',
|
||||
}}
|
||||
widthClassName="max-w-screen-md"
|
||||
breadcrumbs={[
|
||||
{
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import { actions } from 'astro:actions'
|
||||
import { Picture } from 'astro:assets'
|
||||
import { z } from 'astro:content'
|
||||
|
||||
import defaultServiceImage from '../../assets/fallback-service-image.jpg'
|
||||
import Button from '../../components/Button.astro'
|
||||
import MyPicture from '../../components/MyPicture.astro'
|
||||
import TimeFormatted from '../../components/TimeFormatted.astro'
|
||||
import Tooltip from '../../components/Tooltip.astro'
|
||||
import {
|
||||
@@ -72,7 +71,12 @@ const success = !!createResult && !createResult.error
|
||||
<BaseLayout
|
||||
pageTitle="My service suggestions"
|
||||
description="Manage your service suggestions"
|
||||
ogImage={{ template: 'generic', title: 'Service suggestions' }}
|
||||
ogImage={{
|
||||
template: 'generic',
|
||||
title: 'Service suggestions',
|
||||
description: 'Manage your service suggestions',
|
||||
icon: 'ri:service-line',
|
||||
}}
|
||||
widthClassName="max-w-screen-md"
|
||||
breadcrumbs={[
|
||||
{
|
||||
@@ -122,13 +126,13 @@ const success = !!createResult && !createResult.error
|
||||
href={`/service/${suggestion.service.slug}`}
|
||||
class="inline-flex w-full min-w-32 items-center gap-2 hover:underline"
|
||||
>
|
||||
<Picture
|
||||
src={suggestion.service.imageUrl ?? (defaultServiceImage as unknown as string)}
|
||||
<MyPicture
|
||||
src={suggestion.service.imageUrl}
|
||||
fallback="service"
|
||||
alt={suggestion.service.name}
|
||||
width={32}
|
||||
height={32}
|
||||
class="inline-block size-8 min-w-8 shrink-0 rounded-md"
|
||||
formats={['jxl', 'avif', 'webp']}
|
||||
/>
|
||||
<span class="shrink truncate">{suggestion.service.name}</span>
|
||||
</a>
|
||||
|
||||
@@ -65,7 +65,12 @@ const [categories, attributes] = await Astro.locals.banners.tryMany([
|
||||
<BaseLayout
|
||||
pageTitle="New service"
|
||||
description="Suggest a new service to be added to KYCnot.me"
|
||||
ogImage={{ template: 'generic', title: 'New service' }}
|
||||
ogImage={{
|
||||
template: 'generic',
|
||||
title: 'New service',
|
||||
description: 'Suggest a new service to be listed',
|
||||
icon: 'ri:add-circle-line',
|
||||
}}
|
||||
widthClassName="max-w-screen-md"
|
||||
breadcrumbs={[
|
||||
{
|
||||
|
||||
@@ -4,8 +4,7 @@ import { Icon } from 'astro-icon/components'
|
||||
import { Markdown } from 'astro-remote'
|
||||
import { Schema } from 'astro-seo-schema'
|
||||
import { actions } from 'astro:actions'
|
||||
import { Picture } from 'astro:assets'
|
||||
import { head, orderBy, shuffle, sortBy, tail } from 'lodash-es'
|
||||
import { head, orderBy, pick, shuffle, sortBy, tail } from 'lodash-es'
|
||||
|
||||
import AdminOnly from '../../components/AdminOnly.astro'
|
||||
import BadgeSmall from '../../components/BadgeSmall.astro'
|
||||
@@ -17,6 +16,7 @@ import DropdownButton from '../../components/DropdownButton.astro'
|
||||
import DropdownButtonItemForm from '../../components/DropdownButtonItemForm.astro'
|
||||
import DropdownButtonItemLink from '../../components/DropdownButtonItemLink.astro'
|
||||
import FormatTimeInterval from '../../components/FormatTimeInterval.astro'
|
||||
import MyPicture from '../../components/MyPicture.astro'
|
||||
import { makeOgImageUrl, type OgImageAllTemplatesWithProps } from '../../components/OgImage'
|
||||
import ScoreGauge from '../../components/ScoreGauge.astro'
|
||||
import ScoreSquare from '../../components/ScoreSquare.astro'
|
||||
@@ -349,8 +349,12 @@ const getVerificationStepStatusInfo = (status: VerificationStepStatus) => {
|
||||
const itemReviewedId = new URL(`/service/${service.slug}`, Astro.url).href
|
||||
|
||||
const ogImageTemplateData = {
|
||||
template: 'generic',
|
||||
template: 'service',
|
||||
title: service.name,
|
||||
description: service.description,
|
||||
categories: service.categories.map((category) => pick(category, ['name', 'icon'])),
|
||||
score: service.overallScore,
|
||||
imageUrl: service.imageUrl,
|
||||
} satisfies OgImageAllTemplatesWithProps
|
||||
---
|
||||
|
||||
@@ -477,9 +481,8 @@ const ogImageTemplateData = {
|
||||
<div class="flex items-center gap-4">
|
||||
{
|
||||
!!service.imageUrl && (
|
||||
<Picture
|
||||
<MyPicture
|
||||
src={service.imageUrl}
|
||||
formats={['jxl', 'avif', 'webp']}
|
||||
alt={service.name || "Service's logo"}
|
||||
class="size-12 shrink-0 rounded-sm object-contain"
|
||||
width={48}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import { actions } from 'astro:actions'
|
||||
import { Picture } from 'astro:assets'
|
||||
import { sortBy } from 'lodash-es'
|
||||
|
||||
import defaultServiceImage from '../../assets/fallback-service-image.jpg'
|
||||
import AdminOnly from '../../components/AdminOnly.astro'
|
||||
import BadgeSmall from '../../components/BadgeSmall.astro'
|
||||
import InputSubmitButton from '../../components/InputSubmitButton.astro'
|
||||
import InputTextArea from '../../components/InputTextArea.astro'
|
||||
import MyPicture from '../../components/MyPicture.astro'
|
||||
import TimeFormatted from '../../components/TimeFormatted.astro'
|
||||
import Tooltip from '../../components/Tooltip.astro'
|
||||
import { getKarmaTransactionActionInfo } from '../../constants/karmaTransactionActions'
|
||||
@@ -178,7 +177,13 @@ const isCurrentUser = !!Astro.locals.user && user.id === Astro.locals.user.id
|
||||
<BaseLayout
|
||||
pageTitle={`${user.name} - Account`}
|
||||
description="Manage your user profile"
|
||||
ogImage={{ template: 'generic', title: `${user.name} | Account` }}
|
||||
ogImage={{
|
||||
template: 'generic',
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
title: user.displayName || user.name,
|
||||
description: 'User profile page',
|
||||
icon: 'ri:user-3-line',
|
||||
}}
|
||||
widthClassName="max-w-screen-md"
|
||||
className={{
|
||||
main: 'space-y-6',
|
||||
@@ -238,7 +243,13 @@ const isCurrentUser = !!Astro.locals.user && user.id === Astro.locals.user.id
|
||||
<header class="flex items-center gap-4">
|
||||
{
|
||||
user.picture ? (
|
||||
<img src={user.picture} alt="" class="ring-day-500/30 size-16 rounded-full ring-2" />
|
||||
<MyPicture
|
||||
src={user.picture}
|
||||
alt=""
|
||||
class="ring-day-500/30 size-16 rounded-full ring-2"
|
||||
width={64}
|
||||
height={64}
|
||||
/>
|
||||
) : (
|
||||
<div class="bg-day-500/10 ring-day-500/30 text-day-400 flex size-16 items-center justify-center rounded-full ring-2">
|
||||
<Icon name="ri:user-3-line" class="size-8" />
|
||||
@@ -555,8 +566,9 @@ const isCurrentUser = !!Astro.locals.user && user.id === Astro.locals.user.id
|
||||
href={`/service/${affiliation.service.slug}`}
|
||||
class="text-day-300 group flex min-w-32 items-center gap-2 text-sm"
|
||||
>
|
||||
<Picture
|
||||
src={affiliation.service.imageUrl ?? (defaultServiceImage as unknown as string)}
|
||||
<MyPicture
|
||||
src={affiliation.service.imageUrl}
|
||||
fallback="service"
|
||||
alt={affiliation.service.name}
|
||||
width={40}
|
||||
height={40}
|
||||
|
||||
Reference in New Issue
Block a user