map: replace hardcoded Google Maps with universal directions picker (geo:, OSM, Google, Apple)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
shakespeare.diy 2026-07-23 20:46:53 +07:00
parent bb89a6d3a5
commit 5edf4d2a9d

View file

@ -326,6 +326,80 @@ function StashDrawer() {
);
}
// Universal directions picker — works on GrapheneOS, iOS, any browser
function DirectionsMenu({ lat, lon, label }: { lat: string; lon: string; label: string }) {
const [open, setOpen] = useState(false);
const options = [
{
name: 'Maps (device default)',
sub: 'Opens your default maps app',
href: `geo:${lat},${lon}?q=${lat},${lon}(${label})`,
emoji: '📍',
},
{
name: 'OpenStreetMap',
sub: 'Privacy-friendly, no account',
href: `https://www.openstreetmap.org/?mlat=${lat}&mlon=${lon}&zoom=17`,
emoji: '🗺️',
},
{
name: 'Google Maps',
sub: 'Opens in browser or app',
href: `https://maps.google.com/?q=${lat},${lon}`,
emoji: '🔵',
},
{
name: 'Apple Maps',
sub: 'iOS & macOS',
href: `https://maps.apple.com/?ll=${lat},${lon}&q=${label}`,
emoji: '🍎',
},
];
return (
<>
<Button
variant="outline"
className="h-12 w-full font-bold text-sm border-white/20 hover:bg-white/10 rounded-xl"
onClick={() => setOpen(true)}
>
<Navigation className="h-4 w-4 mr-2 text-[#ccff00]" />
Directions
</Button>
{open && (
<>
{/* Backdrop */}
<div className="fixed inset-0 z-[2000]" onClick={() => setOpen(false)} />
{/* Menu */}
<div className="absolute bottom-14 right-0 z-[2001] bg-black/95 border border-white/15 rounded-2xl shadow-2xl overflow-hidden w-64 backdrop-blur">
<div className="px-4 py-3 border-b border-white/8">
<p className="text-xs font-black uppercase tracking-widest text-white/50">Open with</p>
</div>
{options.map(opt => (
<a
key={opt.name}
href={opt.href}
target="_blank"
rel="noopener noreferrer"
onClick={() => setOpen(false)}
className="flex items-center gap-3 px-4 py-3 hover:bg-white/8 transition-colors"
>
<span className="text-xl w-7 text-center">{opt.emoji}</span>
<div className="flex-1 min-w-0">
<div className="text-sm font-bold text-white">{opt.name}</div>
<div className="text-[10px] text-white/40">{opt.sub}</div>
</div>
</a>
))}
</div>
</>
)}
</>
);
}
// Bottom sheet shown when a map marker is tapped
function SpotBottomSheet({ spot, onClose }: { spot: SkateSpot; onClose: () => void }) {
const navigate = useNavigate();
@ -414,15 +488,13 @@ function SpotBottomSheet({ spot, onClose }: { spot: SkateSpot; onClose: () => vo
</Button>
{spot.geohash && (() => {
const { latitude, longitude } = ngeohash.decode(spot.geohash);
const lat = latitude.toFixed(6);
const lon = longitude.toFixed(6);
const label = encodeURIComponent(spot.title);
return (
<Button
variant="outline"
className="h-12 font-bold text-sm border-white/20 hover:bg-white/10 rounded-xl"
onClick={() => window.open(`https://maps.google.com/?q=${latitude},${longitude}`, '_blank')}
>
<Navigation className="h-4 w-4 mr-2 text-[#ccff00]" />
Directions
</Button>
<div className="relative">
<DirectionsMenu lat={lat} lon={lon} label={label} />
</div>
);
})()}
</div>