chore(lint): clear all no-unused-vars warnings (position-precise)

Removed/renamed the remaining 39 unused locals/dead code, file-by-file:
- unused destructured bindings/params (remove the binding, keep the call)
- unused single-line pure consts and dead functions/interfaces removed
- deleted the dead _FeedItem component and its now-unused imports
- amber _readChunks self-call fixed; SpotMap popup call site updated

Warnings: 182 -> 7 (only react-refresh/only-export-components remain).
no-explicit-any=0, exhaustive-deps=0, no-unused-vars=0.
Gate: 101 tests, 0 errors, boot smoke green. AGENTS.md baseline + guidance updated.
This commit is contained in:
SkateSpots 2026-09-26 22:20:21 +07:00
parent 721a5d4b67
commit 6def78fffe
27 changed files with 37 additions and 169 deletions

View file

@ -79,15 +79,16 @@ session with **no other memory of this project**. So:
- **Lazy routes**: every route uses `lazyWithRetry` (`src/lib/lazyWithRetry.ts`) —
retries, then one full reload per session, so a chunk-load failure self-heals
instead of showing the error boundary.
- **Baselines** (verified 2026-09-26): `npm test` → 0 errors, **46 warnings**
(39 `no-unused-vars`, 7 `react-refresh`), **101 tests**, build ok.
- **Baselines** (verified 2026-09-26): `npm test` → 0 errors, **7 warnings**
(all `react-refresh/only-export-components`), **101 tests**, build ok.
`package.json` version **1.4.5**.
- ✅ `no-explicit-any` = 0; ✅ `react-hooks/exhaustive-deps` = 0 (shared getters
are `useCallback`-memoised; intentional mount-only effects carry a justified
`eslint-disable-next-line` with a comment).
- ✅ `no-explicit-any` = 0 · ✅ `react-hooks/exhaustive-deps` = 0 ·
✅ `no-unused-vars` = 0. Remaining: `react-refresh` (dev-only DX — files
exporting non-components next to components; fix by moving the export out).
- ⚠️ Do **not** mass-fix `no-unused-vars` by name or blanket rename — shadowed
names (`isUnfollowing`) and typed shorthand destructuring make it unsafe.
Fix file-by-file, position-precise, opportunistically.
names and typed shorthand destructuring make it unsafe. Fix position-precise.
Renaming a component to `_Name` also trips `rules-of-hooks` — delete it or
suppress instead.
- **Untracked local junk — do NOT commit**: `assets/`, `resources/`,
`cvm-server/`, `bun.lock`.

View file

@ -102,7 +102,7 @@ export function FollowListManager() {
const { user } = useCurrentUser();
const { data: following = [], isLoading: followingLoading } = useFollowing();
const { data: followers = [], isLoading: followersLoading } = useFollowers();
const { unfollow, followByIdentifier, isFollowing, isUnfollowing } = useFollowActions();
const { unfollow, followByIdentifier, isFollowing } = useFollowActions();
const [followInput, setFollowInput] = useState('');

View file

@ -14,7 +14,7 @@ import { useToast } from '@/hooks/useToast';
import { isAdminPubkey } from '@/lib/admins';
import { genUserName } from '@/lib/genUserName';
import { getSkateboardRating, getTrustLevel, SELLER_TRUST_BADGES } from '@/lib/skateboardRating';
import { GEAR_CONDITIONS, GEAR_CATEGORIES, PRICE_TYPES, formatPrice, type MarketplaceItem } from '@/lib/types';
import { GEAR_CONDITIONS, GEAR_CATEGORIES, formatPrice, type MarketplaceItem } from '@/lib/types';
import { MessageCircle, MapPin, Search, CheckCircle, ShieldCheck, AlertTriangle, MoreVertical, Flag, Trash2, RotateCcw, Loader2 } from 'lucide-react';
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator } from '@/components/ui/dropdown-menu';
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from '@/components/ui/alert-dialog';
@ -28,7 +28,7 @@ interface GearSwapItemCardProps {
isFollowing?: boolean;
}
export function GearSwapItemCard({ item, trustScore, salesCount, isFollowing }: GearSwapItemCardProps) {
export function GearSwapItemCard({ item, trustScore, salesCount }: GearSwapItemCardProps) {
const { user } = useCurrentUser();
const navigate = useNavigate();
const author = useAuthor(item.pubkey);

View file

@ -34,7 +34,7 @@ interface LanguageToggleProps {
* Language toggle dropdown for switching between English and Spanish
*/
export function LanguageToggle({ compact = false, className, variant = 'ghost' }: LanguageToggleProps) {
const { language, setLanguage, t } = useTranslation();
const { language, setLanguage } = useTranslation();
const currentLanguage = languages.find(l => l.code === language) || languages[0];

View file

@ -155,7 +155,7 @@ const NostrProvider: React.FC<NostrProviderProps> = (props) => {
return routes;
},
eventRouter(event: NostrEvent) {
eventRouter(_event: NostrEvent) {
// `resolveRelays` applies the blocklist and adds local relays.
const resolved = resolveRelays(relayMetadata.current);
const writeRelays = resolved.write.length > 0

View file

@ -122,7 +122,7 @@ export function QualityMeter({
return items;
}, [type, hasLocation, hasPhotos, photoCount, hasDescription, descriptionLength, hasPhone, hasWebsite, hasHours, hasSpotType]);
const { score, maxScore, completedItems, totalItems } = useMemo(() => {
const { score, maxScore } = useMemo(() => {
const completed = checks.filter(c => c.completed);
const total = checks.reduce((sum, c) => sum + c.points, 0);
const current = completed.reduce((sum, c) => sum + c.points, 0);

View file

@ -50,7 +50,6 @@ export function SortDropdown({
onChange,
className,
showProximity = false,
contentType = 'generic',
}: SortDropdownProps) {
const selectedOption = SORT_OPTIONS[value];
const Icon = selectedOption.icon;

View file

@ -661,8 +661,7 @@ function createPopupContent(
function createEnhancedPopupContent(
spot: SkateSpot,
marketplaceItems: MarketplaceItem[],
onViewDetails: () => void,
weatherData?: SpotWeatherData
onViewDetails: () => void
): HTMLElement {
const container = document.createElement('div');
container.className = 'p-2 min-w-[250px] max-w-[350px]';
@ -1122,7 +1121,7 @@ export function SpotMap({
// Use enhanced popup if there are marketplace items, otherwise use standard
const popupContent = hasGear
? createEnhancedPopupContent(primarySpot, group.marketplaceItems, () => handleViewDetails(primarySpot), spotWeather)
? createEnhancedPopupContent(primarySpot, group.marketplaceItems, () => handleViewDetails(primarySpot))
: createPopupContent(primarySpot, () => handleViewDetails(primarySpot), spotWeather);
marker.bindPopup(popupContent, {

View file

@ -23,7 +23,6 @@ import {
} from '@/components/ui/alert-dialog';
import { useCurrentUser } from '@/hooks/useCurrentUser';
import { useNostrPublish } from '@/hooks/useNostrPublish';
import { useAuthor } from '@/hooks/useAuthor';
import { useToast } from '@/hooks/useToast';
import { useQueryClient } from '@tanstack/react-query';
import { SKATE_SPOT_KIND, type SkateSpot } from '@/lib/types';
@ -47,7 +46,6 @@ export function SpotOwnership({ spot }: SpotOwnershipProps) {
const { mutateAsync: publishEvent, isPending: isPublishing } = useNostrPublish();
const { toast } = useToast();
const queryClient = useQueryClient();
const ownerProfile = useAuthor(spot.pubkey);
const [transferOpen, setTransferOpen] = useState(false);
const [claimOpen, setClaimOpen] = useState(false);

View file

@ -24,7 +24,7 @@ interface TourCardProps {
compact?: boolean;
}
function getCategoryIcon(category: TourCategory) {
function _getCategoryIcon(category: TourCategory) {
switch (category) {
case 'local':
return <Route className="h-4 w-4" />;

View file

@ -86,7 +86,6 @@ const WalletContent = forwardRef<HTMLDivElement, {
setAddDialogOpen: (open: boolean) => void;
}>(({
webln,
hasNWC,
connections,
connectionInfo,
activeConnection,

View file

@ -28,7 +28,7 @@ const MnemonicSignupDialog: React.FC<MnemonicSignupDialogProps> = ({
const [showMnemonic, setShowMnemonic] = useState(true);
const [verifyIndices, setVerifyIndices] = useState<number[]>([]);
const [verifyInputs, setVerifyInputs] = useState<Record<number, string>>({});
const [isVerified, setIsVerified] = useState(false);
const [, setIsVerified] = useState(false);
const login = useLoginActions();
// Generate new mnemonic

View file

@ -142,7 +142,6 @@ function CashuWalletCardNative() {
balances,
mints,
totalBalance,
isInitialized,
isLoading: walletLoading,
error: walletError,
mnemonic,
@ -192,7 +191,7 @@ function CashuWalletCardNative() {
sentTokenQRCode,
} = useTokenOperations({ coco, activeMintUrl, generateQRCode, getEncodedToken });
const { history, isLoading: historyLoading } = useHistoryManager({
const { history } = useHistoryManager({
coco,
repositories,
mints,

View file

@ -23,24 +23,6 @@ export interface BTCMapMerchant {
};
}
interface BTCMapResponse {
type: string;
features: Array<{
type: string;
geometry: {
type: string;
coordinates: [number, number]; // [lon, lat]
};
properties: {
id: string;
name?: string;
'payment:lightning'?: string;
'payment:onchain'?: string;
[key: string]: string | undefined;
};
}>;
}
/**
* Fetch Bitcoin-accepting merchants from BTCMap API
* Uses bounding box to get merchants in the visible map area

View file

@ -114,7 +114,7 @@ export function useFollowActions() {
return targetPubkey;
},
onSuccess: (targetPubkey) => {
onSuccess: (_targetPubkey) => {
// Invalidate queries to refetch
queryClient.invalidateQueries({ queryKey: ['following', user?.pubkey] });
queryClient.invalidateQueries({ queryKey: ['trust-score'] });

View file

@ -62,7 +62,7 @@ interface LastListingRecord {
export function useListingRestrictions(linkedSpotPubkey?: string): ListingRestrictions {
const { nostr } = useNostr();
const { user, metadata } = useCurrentUser();
const [lastListingRecord, setLastListingRecord] = useLocalStorage<LastListingRecord | null>(
const [lastListingRecord] = useLocalStorage<LastListingRecord | null>(
'last-listing-record',
null
);

View file

@ -22,7 +22,6 @@ interface UseHistoryManagerProps {
export function useHistoryManager({
coco,
repositories,
mints,
}: UseHistoryManagerProps) {
const [history, setHistory] = useState<HistoryEntry[]>([]);
const [isLoading, setIsLoading] = useState(false);

View file

@ -649,7 +649,7 @@ export function decodeAmberResult(result: string): string {
const reader = ds.readable.getReader();
const chunks: Uint8Array[] = [];
const readChunks = async (): Promise<string> => {
const _readChunks = async (): Promise<string> => {
const { done, value } = await reader.read();
if (done) {
const combined = new Uint8Array(chunks.reduce((acc, c) => acc + c.length, 0));
@ -661,7 +661,7 @@ export function decodeAmberResult(result: string): string {
return new TextDecoder().decode(combined);
}
chunks.push(value);
return readChunks();
return _readChunks();
};
// For sync usage, we'll just return the raw result

View file

@ -21,7 +21,6 @@ import { useAuthor } from '@/hooks/useAuthor';
import { useCurrentUser } from '@/hooks/useCurrentUser';
import { DeleteEntityDialog } from '@/components/DeleteEntityDialog';
import { isAdminPubkey } from '@/lib/admins';
import { genUserName } from '@/lib/genUserName';
import { COACH_SKILL_LABELS, COACH_SKILL_DESCRIPTIONS, FILMING_SERVICE_LABELS, FILMING_SERVICE_DESCRIPTIONS, FILMING_SKILL_LABELS, FILMING_SKILL_DESCRIPTIONS, EDITOR_SERVICE_LABELS, EDITOR_SERVICE_DESCRIPTIONS, EDITOR_SKILL_LABELS, EDITOR_SKILL_DESCRIPTIONS, PHOTO_SERVICE_LABELS, PHOTO_SERVICE_DESCRIPTIONS, PHOTO_CAMERA_LABELS, PHOTO_LENS_LABELS, PHOTO_LIGHTING_LABELS, PHOTO_SUPPORT_LABELS, PHOTO_STYLE_LABELS, BUILDER_SERVICE_LABELS, BUILDER_SERVICE_DESCRIPTIONS, BUILDER_SKILL_LABELS, BUILDER_SCALE_LABELS, ARTIST_SERVICE_LABELS, ARTIST_SERVICE_DESCRIPTIONS, ARTIST_MEDIUM_LABELS, ARTIST_TOOL_LABELS, GRAFFITI_STYLE_LABELS, GRAFFITI_STYLE_DESCRIPTIONS, GRAFFITI_MEDIUM_LABELS, GRAFFITI_CONTEXT_LABELS, TATTOO_STYLE_LABELS, TATTOO_STYLE_DESCRIPTIONS, TATTOO_METHOD_LABELS, TRAINER_FOCUS_LABELS, TRAINER_FOCUS_DESCRIPTIONS, TRAINER_INJURY_LABELS, TRAINER_CREDENTIAL_LABELS, MUSICIAN_GENRE_LABELS, MUSICIAN_INSTRUMENT_LABELS, JUDGE_FORMAT_LABELS, JUDGE_SPECIALTY_LABELS, JUDGE_LEVEL_LABELS, SKATE_COACH_KIND, SKATE_FILMER_KIND, SKATE_EDITOR_KIND, SKATE_PHOTOGRAPHER_KIND, SKATE_BUILDER_KIND, SKATE_ARTIST_KIND, SKATE_GRAFFITI_KIND, SKATE_TATTOO_KIND, SKATE_TRAINER_KIND, SKATE_MUSICIAN_KIND, SKATE_JUDGE_KIND } from '@/lib/types';
import { sanitizeUrl, sanitizeTel, sanitizeEmail, urlDomain } from '@/lib/sanitizeUrl';
import { OfficialBadge } from '@/components/OfficialBadge';

View file

@ -2,7 +2,7 @@
import { useParams, Link, useNavigate } from 'react-router-dom';
import { useSeoMeta } from '@unhead/react';
import { useNostr } from '@nostrify/react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query';
import { Layout } from '@/components/Layout';
import { PageError } from '@/components/ErrorState';
import { SpotCard, SpotCardSkeleton } from '@/components/SpotCard';
@ -51,7 +51,6 @@ export default function CollectionDetailPage() {
const { deleteCollection, isPending: isDeleting } = useDeleteSpotCollection();
const { toast } = useToast();
const navigate = useNavigate();
const queryClient = useQueryClient();
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const [removingSpotId, setRemovingSpotId] = useState<string | null>(null);

View file

@ -31,12 +31,6 @@ function labCraftTotal(s: {
);
}
function formatSats(sats: number): string {
if (sats >= 1_000_000) return `${(sats / 1_000_000).toFixed(1)}M`;
if (sats >= 1_000) return `${Math.round(sats / 1_000)}k`;
return sats.toLocaleString();
}
const CHART_TOOLTIP_STYLE = {
backgroundColor: '#0d0d0d',
border: '1px solid rgba(255,255,255,0.1)',

View file

@ -48,7 +48,7 @@ export default function CreateListingPage() {
// Anti-spam restrictions
const restrictions = useListingRestrictions(linkedSpot?.pubkey);
const [feePaid, setFeePaid] = useState(false);
const [lastListingRecord, setLastListingRecord] = useLocalStorage<{ timestamp: number; spotOwnerPubkey?: string } | null>(
const [, setLastListingRecord] = useLocalStorage<{ timestamp: number; spotOwnerPubkey?: string } | null>(
'last-listing-record',
null
);

View file

@ -33,14 +33,12 @@ import { ConfirmMembershipCard } from '@/components/ConfirmMembershipCard';
import { MemberVisibilityControl } from '@/components/MemberVisibilityControl';
import { LinkedLabCard } from '@/components/CrossLinkCards';
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from '@/components/ui/alert-dialog';
import { useCollectiveMembers } from '@/hooks/useCollectiveMembers';
import { useCrewRole } from '@/hooks/useCrewRole';
import { useCrewModeration, useHideCrewPost } from '@/hooks/useCrewModeration';
import { useHideCrewPost } from '@/hooks/useCrewModeration';
import { CrewInviteDialog } from '@/components/CrewInviteDialog';
import { DeleteEntityDialog } from '@/components/DeleteEntityDialog';
import { useToast } from '@/hooks/useToast';
import { NoteContent } from '@/components/NoteContent';
import { useCrew } from '@/hooks/useCrews';
import { useCrewFeed } from '@/hooks/useCrewFeed';
import { useAuthor } from '@/hooks/useAuthor';
@ -49,83 +47,9 @@ import { genUserName } from '@/lib/genUserName';
import { SKATE_CREW_KIND, SHOW_CREW_TREASURY, CREW_LOOKING_FOR_LABELS, type CrewRole } from '@/lib/types';
import { sanitizeUrl, urlDomain } from '@/lib/sanitizeUrl';
import NotFound from './NotFound';
import { Users, MapPin, ArrowLeft, Calendar, Globe, Instagram, Edit, MessageSquare, Zap, ShieldCheck, EyeOff, Music } from 'lucide-react';
import { Users, MapPin, ArrowLeft, Calendar, Globe, Instagram, Edit, MessageSquare, Zap, ShieldCheck, Music } from 'lucide-react';
import type { NostrEvent } from '@nostrify/nostrify';
function FeedItem({
event,
canModerate,
onHide,
isHiding,
}: {
event: NostrEvent;
canModerate?: boolean;
onHide?: (event: NostrEvent) => void;
isHiding?: boolean;
}) {
const author = useAuthor(event.pubkey);
const metadata = author.data?.metadata;
const name = metadata?.name ?? genUserName(event.pubkey);
const npub = nip19.npubEncode(event.pubkey);
return (
<div className="rounded-xl border border-white/5 bg-black/40 p-3">
<div className="mb-1.5 flex items-center gap-2">
<Link to={`/${npub}`}>
<Avatar className="h-7 w-7">
<AvatarImage src={metadata?.picture} alt={name} />
<AvatarFallback className="text-[10px]">{name.slice(0, 2).toUpperCase()}</AvatarFallback>
</Avatar>
</Link>
<Link to={`/${npub}`} className="text-sm font-bold text-gray-200 hover:text-white truncate">
{name}
</Link>
<span className="ml-auto text-[11px] text-gray-500">
{new Date(event.created_at * 1000).toLocaleDateString()}
</span>
{canModerate && onHide && (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
type="button"
variant="ghost"
size="icon"
className="h-6 w-6 shrink-0 text-gray-500 hover:text-red-400"
disabled={isHiding}
aria-label="Hide this post"
title="Hide from crew feed (admin)"
>
<EyeOff className="h-3.5 w-3.5" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Hide this post?</AlertDialogTitle>
<AlertDialogDescription>
As a crew moderator you can hide this post from the crew feed for everyone.
The original note stays on Nostr — this only removes it from this crew's feed.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={() => onHide(event)}
className="bg-red-600 hover:bg-red-700"
>
Hide post
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)}
</div>
<div className="whitespace-pre-wrap break-words text-sm text-gray-300">
<NoteContent event={event} className="text-sm" />
</div>
</div>
);
}
export default function CrewDetailPage() {
const { pubkey, identifier } = useParams<{ pubkey: string; identifier: string }>();
const { data: crew, isLoading, error, refetch } = useCrew(identifier || '', pubkey);
@ -143,27 +67,21 @@ export default function CrewDetailPage() {
// Feed = founder + listed members
const feedAuthors = crew ? [crew.pubkey, ...crew.members] : [];
const { data: feed, isLoading: feedLoading } = useCrewFeed(feedAuthors);
const { data: feed } = useCrewFeed(feedAuthors);
// Crew-hosted events (jams, comps, meetups).
const { data: crewEvents } = useCrewEvents(crew?.pubkey, crew?.identifier);
// Moderation — founder + admins can hide feed posts (NIP-32 labels).
const crewCoord = crew ? `${SKATE_CREW_KIND}:${crew.pubkey}:${crew.identifier}` : '';
const moderators = crew ? Array.from(new Set([crew.pubkey, ...crew.admins])) : [];
const { data: hiddenIds } = useCrewModeration({
crewCoord,
moderators,
enabled: !!crew,
});
const { mutateAsync: hidePost, isPending: isHiding } = useHideCrewPost(crewCoord);
const { mutateAsync: hidePost } = useHideCrewPost(crewCoord);
// Unread dot on the Chat tab — cheap, member-only, no background decryption.
const isCrewMember = !!user && !!crew &&
(user.pubkey === crew.pubkey || crew.members.includes(user.pubkey));
const { hasUnread: chatHasUnread } = useCrewChatUnread(crew ?? undefined, isCrewMember);
const handleHidePost = async (ev: NostrEvent) => {
const _handleHidePost = async (ev: NostrEvent) => {
try {
await hidePost({ postId: ev.id, postAuthor: ev.pubkey });
toast({ title: 'Post hidden', description: 'Removed from the crew feed for everyone.' });

View file

@ -491,7 +491,6 @@ function LabProfileCard({ profile }: { profile: LabProfile }) {
}
function NewToNostrSection() {
const { t } = useTranslation();
const { user } = useCurrentUser();
// Don't show this section if user is already logged in

View file

@ -172,7 +172,7 @@ function MiniStashCard({ item, onDelete }: { item: StashItem; onDelete: () => vo
// Stash drawer component
function StashDrawer() {
const { user } = useCurrentUser();
const { items, stats, isLoading, addItem, removeItem, isEncryptionAvailable } = useStash();
const { items, stats, isLoading, removeItem, isEncryptionAvailable } = useStash();
const author = useAuthor(user?.pubkey || '');
const metadata = author.data?.metadata;
const displayName = metadata?.name ?? genUserName(user?.pubkey || '');
@ -625,7 +625,7 @@ export default function MapPage() {
const showWelcomeBanner = !user && !welcomeDismissed;
// Track Sasquatch panel open state so the Add Spot FAB can step aside
const [sasquatchPanelOpen, setSasquatchPanelOpen] = useState(false);
const [, setSasquatchPanelOpen] = useState(false);
useEffect(() => {
const handler = (e: Event) => {
setSasquatchPanelOpen((e as CustomEvent<{ open: boolean }>).detail.open);
@ -771,7 +771,7 @@ export default function MapPage() {
// Fetch BTC Map places when enabled
const { data: btcMapPlaces, isLoading: btcMapLoading, error: btcMapError } = useBtcMapPlaces({
const { data: btcMapPlaces, isLoading: btcMapLoading } = useBtcMapPlaces({
lat: mapCenter.lat,
lon: mapCenter.lon,
radiusKm: 50, // 50km radius
@ -881,7 +881,7 @@ export default function MapPage() {
}, [allLocations]);
// Fetch weather data for all spots
const { data: weatherData, isLoading: weatherLoading } = useMultipleSpotWeather(
const { data: weatherData } = useMultipleSpotWeather(
spotGeohashes,
weatherEnabled
);

View file

@ -119,7 +119,7 @@ function fillTextFit(ctx: CanvasRenderingContext2D, text: string, x: number, y:
}
// Draw a labeled row: small muted label above, value below
function drawLabeledRow(
function _drawLabeledRow(
ctx: CanvasRenderingContext2D,
label: string, value: string,
x: number, y: number, maxW: number,
@ -553,7 +553,7 @@ function memberSince(createdAt: number) {
}
// Verification URL — scanning this on a phone opens the profile on skate-spots.org
function verifyUrl(pubkey: string) {
function _verifyUrl(pubkey: string) {
if (!pubkey) return 'https://skate-spots.org';
return `https://skate-spots.org/${nip19.npubEncode(pubkey)}`;
}
@ -1490,7 +1490,7 @@ export default function RiderCardPage() {
}
// ─── Inline award form (used on the Award tab) ────────────────────────────────
function AwardBadgeInline({ issuerPubkey, onSuccess }: { issuerPubkey: string; onSuccess: () => void }) {
function AwardBadgeInline({ issuerPubkey: _issuerPubkey, onSuccess }: { issuerPubkey: string; onSuccess: () => void }) {
const { mutate: publishEvent, isPending } = useNostrPublish();
const { toast } = useToast();
const [recipientNpub, setRecipientNpub] = useState('');

View file

@ -12,7 +12,6 @@ import { CVMHealthCheck } from '@/components/CVMHealthCheck';
import { LanguageSelector } from '@/components/LanguageToggle';
import { DesktopNavManager } from '@/components/DesktopNavManager';
import { MyDataTab } from '@/components/MyDataTab';
import { useTranslation } from '@/contexts/LanguageContext';
import { useQueryClient } from '@tanstack/react-query';
import { useNostr } from '@nostrify/react';
import { NSchema as n } from '@nostrify/nostrify';
@ -544,7 +543,6 @@ function RelaysScreen({ onBack }: { onBack: () => void }) {
// ─── Appearance sub-screen ────────────────────────────────────────────────────
function AppearanceScreen({ onBack }: { onBack: () => void }) {
const { t } = useTranslation();
return (
<div>
<BackHeader title="Appearance" onBack={onBack} />
@ -1148,21 +1146,6 @@ export default function SettingsPage() {
const handleNavigate = (s: Section) => setSection(s);
const handleBack = () => setSection(null);
const sectionTitles: Record<NonNullable<Section>, string> = {
profile: 'Edit Profile',
follows: 'Follows',
nip05: 'NIP-05 Verification',
security: 'Security',
mydata: 'My Data',
language: 'Language',
appearance: 'Appearance',
relays: 'Relays',
crewnetwork: 'Crew Network',
wallet: 'Wallet',
stash: 'My Stash',
advanced: 'Advanced',
};
return (
<Layout>
<div className="container px-4 py-5 max-w-lg mx-auto">