refactor(components): improve type safety and simplify IconButton

- Add proper type guards in ContentWithResolvedProfiles to avoid type assertions
- Remove href/link functionality from IconButton component for simplification
- Replace 'as any' with proper type narrowing using type predicates
This commit is contained in:
Gigi
2025-10-03 01:43:13 +02:00
parent 0964156bcc
commit 0b058440bc
4 changed files with 46 additions and 32 deletions

View File

@@ -0,0 +1,16 @@
---
description: documentation that's useful when dealing with bookmark events (kind:10003 or kind:30003) or anything related to NIP-51
alwaysApply: false
---
Read the nostrbook to understand how bookmarks work:
- https://nostrbook.dev/kinds/10003
- https://nostrbook.dev/kinds/30003
They are defined in NIP-51:
- https://github.com/nostr-protocol/nips/blob/master/51.md
Also refer to the applesauce bookmark helpers:
- https://github.com/hzrd149/applesauce/blob/17c9dbb0f2c263e2ebd01729ea2fa138eca12bd1/packages/core/src/helpers/bookmarks.ts
Make sure to always use applesauce, and use it properly.

19
kind-icons.txt Normal file
View File

@@ -0,0 +1,19 @@
kind:0 = fa-circle-user
kind:1 = fa-feather
kind:6 = fa-retweet
kind:7 = fa-heart
kind:20 = fa-image
kind:21 = fa-video
kind:22 = fa-video
kind:1063 = fa-file
kind:1337 = fa-laptop-code
kind:1617 = fa-code-pull-request
kind:1621 = fa-bug
kind:1984 = fa-exclamation-triangle
kind:9735 = fa-bolt
kind:9321 = fa-cloud-bolt
kind:9802 = fa-highlighter
kind:30023 = fa-newspaper
kind:10000 = fa-eye-slash
kind:10001 = fa-thumbtack
kind:10003 = fa-bookmark

View File

@@ -11,17 +11,19 @@ const ContentWithResolvedProfiles: React.FC<Props> = ({ content }) => {
const matches = extractNprofilePubkeys(content) const matches = extractNprofilePubkeys(content)
const decoded = matches const decoded = matches
.map((m) => { .map((m) => {
try { return decode(m) } catch { return undefined } try { return decode(m) } catch { return undefined as undefined }
}) })
.filter(Boolean) .filter((v): v is ReturnType<typeof decode> => Boolean(v))
const lookups = decoded.map((res) => getPubkeyFromDecodeResult(res as any)).filter(Boolean) as string[] const lookups = decoded
.map((res) => getPubkeyFromDecodeResult(res))
.filter((v): v is string => typeof v === 'string')
const profiles = lookups.map((pubkey) => ({ pubkey, profile: useEventModel(Models.ProfileModel, [pubkey]) })) const profiles = lookups.map((pubkey) => ({ pubkey, profile: useEventModel(Models.ProfileModel, [pubkey]) }))
let rendered = content let rendered = content
matches.forEach((m, i) => { matches.forEach((m, i) => {
const pk = getPubkeyFromDecodeResult(decoded[i] as any) const pk = getPubkeyFromDecodeResult(decoded[i])
const found = profiles.find((p) => p.pubkey === pk) const found = profiles.find((p) => p.pubkey === pk)
const name = found?.profile?.name || found?.profile?.display_name || found?.profile?.nip05 || `${pk?.slice(0,8)}...` const name = found?.profile?.name || found?.profile?.display_name || found?.profile?.nip05 || `${pk?.slice(0,8)}...`
if (name) rendered = rendered.replace(m, `@${name}`) if (name) rendered = rendered.replace(m, `@${name}`)

View File

@@ -9,9 +9,6 @@ interface IconButtonProps {
ariaLabel?: string ariaLabel?: string
variant?: 'primary' | 'success' | 'ghost' variant?: 'primary' | 'success' | 'ghost'
size?: number size?: number
href?: string
target?: string
rel?: string
} }
const IconButton: React.FC<IconButtonProps> = ({ const IconButton: React.FC<IconButtonProps> = ({
@@ -20,35 +17,15 @@ const IconButton: React.FC<IconButtonProps> = ({
title, title,
ariaLabel, ariaLabel,
variant = 'ghost', variant = 'ghost',
size = 44, size = 44
href,
target,
rel
}) => { }) => {
const commonProps = {
className: `icon-button ${variant}`,
title,
'aria-label': ariaLabel || title,
style: { width: size, height: size }
} as const
if (href) {
return (
<a
{...(commonProps as any)}
href={href}
target={target || '_blank'}
rel={rel || 'noopener noreferrer'}
>
<FontAwesomeIcon icon={icon} />
</a>
)
}
return ( return (
<button <button
{...(commonProps as any)} className={`icon-button ${variant}`}
onClick={onClick} onClick={onClick}
title={title}
aria-label={ariaLabel || title}
style={{ width: size, height: size }}
> >
<FontAwesomeIcon icon={icon} /> <FontAwesomeIcon icon={icon} />
</button> </button>