Spot detail: make mapper clickable + add Message Mapper button
The "Tip the Mapper" card now links the avatar/name to the mapper's profile
(/{npub} via the NIP-19 router) and adds a "Message Mapper" button that opens
an encrypted DM (/messages/{npub}). Makes it easy to contact whoever added a
spot — e.g. to ask them to add photos to a photoless submission. The message
button is hidden when you're viewing your own spot.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
parent
aba79900e4
commit
8b080fb7f1
3 changed files with 55 additions and 11 deletions
22
src/lib/admins.ts
Normal file
22
src/lib/admins.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// ─── SkateSpots admin identities ────────────────────────────────────────────
|
||||
//
|
||||
// Single source of truth for who counts as an "admin" across the app
|
||||
// (newsletter publishing, relay admin dashboard, etc.).
|
||||
//
|
||||
// Pubkeys are stored in HEX because that's what `user.pubkey` and Nostr
|
||||
// filters use. The bech32 npub is kept in a comment for human readability.
|
||||
|
||||
/** Hex pubkeys of SkateSpots admins. */
|
||||
export const ADMIN_PUBKEYS: string[] = [
|
||||
// SkateSpots founder
|
||||
// npub1yrnuj56rnen08zp2h9h7p74ghgjx6ma39spmpj6w9hzxywutevsst7k5cx
|
||||
'20e7c953439e66f3882ab96fe0faa8ba246d6fb12c03b0cb4e2dc4623b8bcb21',
|
||||
// npub1e2ayjlmmaksx2rtt0z4jzyskunfwh2lwe89xav4qtnhgu4wxsudsp98a4l
|
||||
'caba497f7beda0650d6b78ab211216e4d2ebabeec9ca6eb2a05cee8e55c6871b',
|
||||
];
|
||||
|
||||
/** Returns true if the given hex pubkey belongs to a SkateSpots admin. */
|
||||
export function isAdminPubkey(pubkey: string | undefined | null): boolean {
|
||||
if (!pubkey) return false;
|
||||
return ADMIN_PUBKEYS.includes(pubkey);
|
||||
}
|
||||
|
|
@ -28,10 +28,7 @@ import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
|
|||
|
||||
// ─── Admin pubkeys allowed to publish newsletter as kind 1 ───────────────────
|
||||
|
||||
const NEWSLETTER_ADMIN_PUBKEYS = [
|
||||
'20e7c953439e66f3882ab96fe0faa8ba246d6fb12c03b0cb4e2dc4623b8bcb21',
|
||||
'caba497f7beda0650d6b78ab211216e4d2ebabeec9ca6eb2a05cee8e55c6871b',
|
||||
];
|
||||
import { isAdminPubkey } from '@/lib/admins';
|
||||
|
||||
// ─── Newsletter issue structure ───────────────────────────────────────────────
|
||||
//
|
||||
|
|
|
|||
|
|
@ -54,7 +54,9 @@ import {
|
|||
Star,
|
||||
Flag,
|
||||
MoreHorizontal,
|
||||
MessageCircle,
|
||||
} from 'lucide-react';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { ReportDialog } from '@/components/ReportDialog';
|
||||
import { WhosSkating } from '@/components/WhosSkating';
|
||||
import { SetStatusDialog } from '@/components/SetStatusDialog';
|
||||
|
|
@ -146,6 +148,8 @@ export default function SpotDetailPage() {
|
|||
|
||||
const metadata = author.data?.metadata;
|
||||
const displayName = metadata?.name ?? genUserName(spot?.pubkey || '');
|
||||
// npub for linking to the mapper's profile / messaging them
|
||||
const mapperNpub = spot?.pubkey ? nip19.npubEncode(spot.pubkey) : '';
|
||||
|
||||
const coordinates = useMemo(() => {
|
||||
if (!spot?.geohash) return null;
|
||||
|
|
@ -910,7 +914,10 @@ export default function SpotDetailPage() {
|
|||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<Link
|
||||
to={`/${mapperNpub}`}
|
||||
className="flex items-center gap-3 mb-4 group rounded-xl -m-1 p-1 hover:bg-white/5 transition-colors"
|
||||
>
|
||||
<Avatar className="h-12 w-12 border-2 border-[#ccff00]/30">
|
||||
<AvatarImage src={metadata?.picture} alt={displayName} />
|
||||
<AvatarFallback className="bg-[#ccff00]/10 text-[#ccff00]">
|
||||
|
|
@ -918,22 +925,40 @@ export default function SpotDetailPage() {
|
|||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="font-medium truncate">{displayName}</p>
|
||||
<p className="font-medium truncate group-hover:text-[#ccff00] transition-colors">
|
||||
{displayName}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground flex items-center gap-1">
|
||||
<Calendar className="h-3 w-3" />
|
||||
{new Date(spot.createdAt * 1000).toLocaleDateString()}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<p className="text-xs text-gray-400 mb-3">
|
||||
Found this spot useful? Send a zap to thank the mapper!
|
||||
</p>
|
||||
|
||||
<ZapButton
|
||||
target={spot.event}
|
||||
className="w-full justify-center bg-[#ccff00] text-black hover:bg-[#ccff00]/90 font-bold"
|
||||
/>
|
||||
<div className="space-y-2">
|
||||
<ZapButton
|
||||
target={spot.event}
|
||||
className="w-full justify-center bg-[#ccff00] text-black hover:bg-[#ccff00]/90 font-bold"
|
||||
/>
|
||||
{/* Message the mapper — e.g. to ask them to add photos.
|
||||
Hidden when you're the owner (can't message yourself). */}
|
||||
{!isOwner && (
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
className="w-full justify-center border-[#ccff00]/40 text-[#ccff00] hover:bg-[#ccff00]/10 hover:text-[#ccff00] font-bold"
|
||||
>
|
||||
<Link to={`/messages/${mapperNpub}`}>
|
||||
<MessageCircle className="h-4 w-4 mr-2" />
|
||||
Message Mapper
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue