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 ───────────────────
|
// ─── Admin pubkeys allowed to publish newsletter as kind 1 ───────────────────
|
||||||
|
|
||||||
const NEWSLETTER_ADMIN_PUBKEYS = [
|
import { isAdminPubkey } from '@/lib/admins';
|
||||||
'20e7c953439e66f3882ab96fe0faa8ba246d6fb12c03b0cb4e2dc4623b8bcb21',
|
|
||||||
'caba497f7beda0650d6b78ab211216e4d2ebabeec9ca6eb2a05cee8e55c6871b',
|
|
||||||
];
|
|
||||||
|
|
||||||
// ─── Newsletter issue structure ───────────────────────────────────────────────
|
// ─── Newsletter issue structure ───────────────────────────────────────────────
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,9 @@ import {
|
||||||
Star,
|
Star,
|
||||||
Flag,
|
Flag,
|
||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
|
MessageCircle,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { nip19 } from 'nostr-tools';
|
||||||
import { ReportDialog } from '@/components/ReportDialog';
|
import { ReportDialog } from '@/components/ReportDialog';
|
||||||
import { WhosSkating } from '@/components/WhosSkating';
|
import { WhosSkating } from '@/components/WhosSkating';
|
||||||
import { SetStatusDialog } from '@/components/SetStatusDialog';
|
import { SetStatusDialog } from '@/components/SetStatusDialog';
|
||||||
|
|
@ -146,6 +148,8 @@ export default function SpotDetailPage() {
|
||||||
|
|
||||||
const metadata = author.data?.metadata;
|
const metadata = author.data?.metadata;
|
||||||
const displayName = metadata?.name ?? genUserName(spot?.pubkey || '');
|
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(() => {
|
const coordinates = useMemo(() => {
|
||||||
if (!spot?.geohash) return null;
|
if (!spot?.geohash) return null;
|
||||||
|
|
@ -910,7 +914,10 @@ export default function SpotDetailPage() {
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<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">
|
<Avatar className="h-12 w-12 border-2 border-[#ccff00]/30">
|
||||||
<AvatarImage src={metadata?.picture} alt={displayName} />
|
<AvatarImage src={metadata?.picture} alt={displayName} />
|
||||||
<AvatarFallback className="bg-[#ccff00]/10 text-[#ccff00]">
|
<AvatarFallback className="bg-[#ccff00]/10 text-[#ccff00]">
|
||||||
|
|
@ -918,22 +925,40 @@ export default function SpotDetailPage() {
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<div className="flex-1 min-w-0">
|
<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">
|
<p className="text-sm text-muted-foreground flex items-center gap-1">
|
||||||
<Calendar className="h-3 w-3" />
|
<Calendar className="h-3 w-3" />
|
||||||
{new Date(spot.createdAt * 1000).toLocaleDateString()}
|
{new Date(spot.createdAt * 1000).toLocaleDateString()}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Link>
|
||||||
|
|
||||||
<p className="text-xs text-gray-400 mb-3">
|
<p className="text-xs text-gray-400 mb-3">
|
||||||
Found this spot useful? Send a zap to thank the mapper!
|
Found this spot useful? Send a zap to thank the mapper!
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
<ZapButton
|
<ZapButton
|
||||||
target={spot.event}
|
target={spot.event}
|
||||||
className="w-full justify-center bg-[#ccff00] text-black hover:bg-[#ccff00]/90 font-bold"
|
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>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue