Map audit: fix dead trusted-first sort + broken stash-card borders (1.2.36)
Two real bugs surfaced auditing the Map page.
1. The "trusted-first" soft sort was silently dead for the default
config. filteredSpots ran a standalone .sort() to float trusted
content up, then IMMEDIATELY ran a second unconditional .sort() by
sortBy — which re-ordered everything and wiped the trust ordering.
For a logged-in user with ≥5 follows the smart default is
trustFilter='trusted' + sortBy='recent', so the documented
"sort trusted first, badge unverified" behaviour never actually
happened. Fixed by folding the trust tier into the MAIN comparator as
its primary key, with the chosen sortBy as the tiebreaker within each
tier. Strict modes (verified/following) still hard-filter as before.
2. MiniStashCard (gear pins in the map drawer) built its border colour
with an interpolated Tailwind class (`border-${colour}-500/40`) that
the JIT compiler can't see, so it was never generated — dead class
emitting an invalid string into the DOM. The inline
style={{ borderColor }} that actually worked was also applied
unconditionally, overriding the intended cyan border on WISHLIST
items with the rarity colour. Now: wishlist keeps its static cyan
border; gear uses the rarity colour via inline style only.
Geolocation (native vs web, denied/timeout/no-provider handling),
batched trust/status/comment/weather queries, and the layer plumbing
into SpotMap were all audited and are sound.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
parent
719dd2c4a8
commit
85cb76b458
2 changed files with 30 additions and 23 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "skate-spots",
|
"name": "skate-spots",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.2.35",
|
"version": "1.2.36",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"homepage": "https://skate-spots.com",
|
"homepage": "https://skate-spots.com",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -98,10 +98,16 @@ function MiniStashCard({ item, onDelete }: { item: StashItem; onDelete: () => vo
|
||||||
const hasImage = item.images && item.images.length > 0;
|
const hasImage = item.images && item.images.length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn(
|
<div
|
||||||
'relative p-3 rounded-lg border-2 bg-black/60 transition-all hover:bg-black/80',
|
className={cn(
|
||||||
isWishlist ? 'border-cyan-500/40' : `border-${rarity === 'common' ? 'gray' : rarity === 'uncommon' ? 'green' : rarity === 'rare' ? 'blue' : rarity === 'epic' ? 'purple' : 'yellow'}-500/40`
|
'relative p-3 rounded-lg border-2 bg-black/60 transition-all hover:bg-black/80',
|
||||||
)} style={{ borderColor: rarityTier.color + '40' }}>
|
// Wishlist keeps a fixed cyan border; gear uses its rarity colour (applied
|
||||||
|
// via inline style below, since Tailwind's JIT can't see dynamically
|
||||||
|
// interpolated `border-${colour}` class names).
|
||||||
|
isWishlist && 'border-cyan-500/40',
|
||||||
|
)}
|
||||||
|
style={isWishlist ? undefined : { borderColor: rarityTier.color + '40' }}
|
||||||
|
>
|
||||||
{/* Wishlist badge */}
|
{/* Wishlist badge */}
|
||||||
{isWishlist && (
|
{isWishlist && (
|
||||||
<Badge className="absolute -top-2 -left-1 bg-cyan-500 text-black text-[9px] px-1.5 py-0">
|
<Badge className="absolute -top-2 -left-1 bg-cyan-500 text-black text-[9px] px-1.5 py-0">
|
||||||
|
|
@ -833,27 +839,28 @@ export default function MapPage() {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Apply trust filter — when 'trusted', sort trusted content to the top
|
// Apply trust filter.
|
||||||
// rather than hiding untrusted content (avoids penalising new users/anon browsers)
|
// • 'trusted' (soft): keep everything visible but float trusted content to
|
||||||
// Strict filtering (verified/following) still hides low-trust content as expected
|
// the top. This must be folded into the MAIN sort below as its primary
|
||||||
if (trustScores && trustFilter !== 'all') {
|
// key — running it as a separate .sort() first is pointless because the
|
||||||
if (trustFilter === 'trusted') {
|
// sort() that follows would immediately re-order everything and wipe it
|
||||||
// Soft mode: sort trusted first, keep everything visible
|
// out (which it silently did for the default trusted+recent combo).
|
||||||
filtered.sort((a, b) => {
|
// • 'verified'/'following' (strict): actually hide low-trust content.
|
||||||
const scoreA = getTrustScore(a.pubkey) ?? -1;
|
const softTrust = trustFilter === 'trusted';
|
||||||
const scoreB = getTrustScore(b.pubkey) ?? -1;
|
if (trustScores && trustFilter !== 'all' && !softTrust) {
|
||||||
const trustedA = scoreA >= 60 ? 1 : 0;
|
filtered = filterByTrustLevel(filtered, trustFilter, getTrustScore);
|
||||||
const trustedB = scoreB >= 60 ? 1 : 0;
|
|
||||||
return trustedB - trustedA;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Strict modes (verified, following) — actually filter
|
|
||||||
filtered = filterByTrustLevel(filtered, trustFilter, getTrustScore);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply sorting
|
// Trust tier (1 = trusted, 0 = not) used as the primary sort key in soft mode.
|
||||||
|
const trustTier = (pubkey: string) => ((getTrustScore(pubkey) ?? -1) >= 60 ? 1 : 0);
|
||||||
|
|
||||||
|
// Apply sorting. In soft-trust mode, trusted content sorts first and the
|
||||||
|
// chosen `sortBy` becomes the tiebreaker WITHIN each trust tier.
|
||||||
filtered.sort((a, b) => {
|
filtered.sort((a, b) => {
|
||||||
|
if (softTrust && trustScores) {
|
||||||
|
const tierDiff = trustTier(b.pubkey) - trustTier(a.pubkey);
|
||||||
|
if (tierDiff !== 0) return tierDiff;
|
||||||
|
}
|
||||||
switch (sortBy) {
|
switch (sortBy) {
|
||||||
case 'trust': {
|
case 'trust': {
|
||||||
const scoreA = getTrustScore(a.pubkey) || 0;
|
const scoreA = getTrustScore(a.pubkey) || 0;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue