mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-06-05 18:21:28 +02:00
refactor: Improve Profile Badges UX
Feed view: - Show all badge thumbnails (removed 4-badge limit) - Entire feed item is clickable to open detail view - Badge count displayed inline Detail view: - Change from grid to vertical list layout - Show one badge per row with horizontal layout - Display: awarded by author, badge image, name, and description - Better readability for badge information
This commit is contained in:
@@ -35,9 +35,9 @@ function parseAddress(aTagValue: string): AddressPointer | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Single badge card component with image, name, and description
|
* Single badge row component with author, image, name, and description
|
||||||
*/
|
*/
|
||||||
function BadgeCard({
|
function BadgeRow({
|
||||||
badgeAddress,
|
badgeAddress,
|
||||||
awardEventId,
|
awardEventId,
|
||||||
}: {
|
}: {
|
||||||
@@ -70,63 +70,55 @@ function BadgeCard({
|
|||||||
const displayTitle = badgeName || badgeIdentifier || "Badge";
|
const displayTitle = badgeName || badgeIdentifier || "Badge";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-3 p-4 rounded-lg border border-border bg-card hover:bg-muted/50 transition-colors">
|
<div className="flex gap-4 items-start p-4 rounded-lg border border-border bg-card hover:bg-muted/50 transition-colors">
|
||||||
{/* Badge Image */}
|
{/* Badge Image */}
|
||||||
<div className="flex items-center justify-center">
|
{badgeImageUrl ? (
|
||||||
{badgeImageUrl ? (
|
<img
|
||||||
<img
|
src={badgeImageUrl}
|
||||||
src={badgeImageUrl}
|
alt={displayTitle}
|
||||||
alt={displayTitle}
|
className="size-16 rounded-lg object-cover flex-shrink-0"
|
||||||
className="size-20 rounded-lg object-cover"
|
loading="lazy"
|
||||||
loading="lazy"
|
/>
|
||||||
/>
|
) : (
|
||||||
) : (
|
<div className="size-16 rounded-lg bg-muted flex items-center justify-center flex-shrink-0">
|
||||||
<div className="size-20 rounded-lg bg-muted flex items-center justify-center">
|
<Award className="size-8 text-muted-foreground" />
|
||||||
<Award className="size-10 text-muted-foreground" />
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Badge Info */}
|
{/* Badge Info */}
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-2 flex-1 min-w-0">
|
||||||
|
{/* Awarded by */}
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-xs text-muted-foreground">Awarded by</span>
|
||||||
|
{awardEvent && <UserName pubkey={awardEvent.pubkey} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Badge Name */}
|
||||||
{badgeEvent ? (
|
{badgeEvent ? (
|
||||||
<ClickableEventTitle
|
<ClickableEventTitle
|
||||||
event={badgeEvent}
|
event={badgeEvent}
|
||||||
className="font-semibold text-foreground text-center line-clamp-2"
|
className="text-lg font-semibold text-foreground"
|
||||||
>
|
>
|
||||||
{displayTitle}
|
{displayTitle}
|
||||||
</ClickableEventTitle>
|
</ClickableEventTitle>
|
||||||
) : (
|
) : (
|
||||||
<h3 className="font-semibold text-foreground text-center line-clamp-2">
|
<h3 className="text-lg font-semibold text-foreground">
|
||||||
{displayTitle}
|
{displayTitle}
|
||||||
</h3>
|
</h3>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Badge Description */}
|
||||||
{badgeDescription && (
|
{badgeDescription && (
|
||||||
<p className="text-xs text-muted-foreground text-center line-clamp-2">
|
<p className="text-sm text-muted-foreground">{badgeDescription}</p>
|
||||||
{badgeDescription}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Award Info */}
|
|
||||||
{awardEvent && (
|
|
||||||
<div className="flex flex-col gap-1 pt-2 border-t border-border">
|
|
||||||
<p className="text-xs text-muted-foreground text-center">
|
|
||||||
Awarded by
|
|
||||||
</p>
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<UserName pubkey={awardEvent.pubkey} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detail renderer for Kind 30008 - Profile Badges (NIP-58)
|
* Detail renderer for Kind 30008 - Profile Badges (NIP-58)
|
||||||
* Shows all badges in a grid layout
|
* Shows all badges in a vertical list
|
||||||
*/
|
*/
|
||||||
export function ProfileBadgesDetailRenderer({
|
export function ProfileBadgesDetailRenderer({
|
||||||
event,
|
event,
|
||||||
@@ -134,7 +126,7 @@ export function ProfileBadgesDetailRenderer({
|
|||||||
const badgePairs = getProfileBadgePairs(event);
|
const badgePairs = getProfileBadgePairs(event);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6 p-6 max-w-6xl mx-auto">
|
<div className="flex flex-col gap-6 p-6 max-w-4xl mx-auto">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h1 className="text-3xl font-bold">Profile Badges</h1>
|
<h1 className="text-3xl font-bold">Profile Badges</h1>
|
||||||
@@ -147,11 +139,11 @@ export function ProfileBadgesDetailRenderer({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Badges Grid */}
|
{/* Badges List */}
|
||||||
{badgePairs.length > 0 ? (
|
{badgePairs.length > 0 ? (
|
||||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
<div className="flex flex-col gap-3">
|
||||||
{badgePairs.map((pair, idx) => (
|
{badgePairs.map((pair, idx) => (
|
||||||
<BadgeCard
|
<BadgeRow
|
||||||
key={idx}
|
key={idx}
|
||||||
badgeAddress={pair.badgeAddress}
|
badgeAddress={pair.badgeAddress}
|
||||||
awardEventId={pair.awardEventId}
|
awardEventId={pair.awardEventId}
|
||||||
|
|||||||
@@ -74,13 +74,10 @@ function BadgeItem({ badgeAddress }: { badgeAddress: string }) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Renderer for Kind 30008 - Profile Badges (NIP-58)
|
* Renderer for Kind 30008 - Profile Badges (NIP-58)
|
||||||
* Shows a few badges with "and N others" pattern
|
* Shows all badge thumbnails, clickable to open detail view
|
||||||
*/
|
*/
|
||||||
export function ProfileBadgesRenderer({ event }: BaseEventProps) {
|
export function ProfileBadgesRenderer({ event }: BaseEventProps) {
|
||||||
const badgePairs = getProfileBadgePairs(event);
|
const badgePairs = getProfileBadgePairs(event);
|
||||||
const MAX_VISIBLE = 4;
|
|
||||||
const visibleBadges = badgePairs.slice(0, MAX_VISIBLE);
|
|
||||||
const remainingCount = Math.max(0, badgePairs.length - MAX_VISIBLE);
|
|
||||||
|
|
||||||
if (badgePairs.length === 0) {
|
if (badgePairs.length === 0) {
|
||||||
return (
|
return (
|
||||||
@@ -95,23 +92,20 @@ export function ProfileBadgesRenderer({ event }: BaseEventProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseEventContainer event={event}>
|
<BaseEventContainer event={event}>
|
||||||
<div className="flex items-center gap-3 flex-wrap">
|
<ClickableEventTitle
|
||||||
{/* Badge Icons */}
|
event={event}
|
||||||
<div className="flex items-center gap-2">
|
className="flex items-center gap-2 flex-wrap hover:opacity-80 transition-opacity"
|
||||||
{visibleBadges.map((pair, idx) => (
|
>
|
||||||
<BadgeItem key={idx} badgeAddress={pair.badgeAddress} />
|
{/* All Badge Thumbnails */}
|
||||||
))}
|
{badgePairs.map((pair, idx) => (
|
||||||
</div>
|
<BadgeItem key={idx} badgeAddress={pair.badgeAddress} />
|
||||||
|
))}
|
||||||
|
|
||||||
{/* Badge Count */}
|
{/* Badge Count */}
|
||||||
<ClickableEventTitle
|
<span className="text-sm text-muted-foreground ml-1">
|
||||||
event={event}
|
|
||||||
className="text-sm text-muted-foreground hover:text-foreground"
|
|
||||||
>
|
|
||||||
{badgePairs.length} {badgePairs.length === 1 ? "badge" : "badges"}
|
{badgePairs.length} {badgePairs.length === 1 ? "badge" : "badges"}
|
||||||
{remainingCount > 0 && ` (${remainingCount} more)`}
|
</span>
|
||||||
</ClickableEventTitle>
|
</ClickableEventTitle>
|
||||||
</div>
|
|
||||||
</BaseEventContainer>
|
</BaseEventContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user