This commit is contained in:
Andras Schmelczer 2026-05-09 10:22:44 +01:00
parent fe46cb3379
commit dd9f00b105
8 changed files with 338 additions and 103 deletions

View file

@ -6,19 +6,16 @@ export function useLicense() {
const [checkingOut, setCheckingOut] = useState(false);
const [error, setError] = useState<string | null>(null);
const startCheckout = useCallback(async (referralCode?: string) => {
trackEvent('Checkout Start', { has_referral: String(!!referralCode) });
const startCheckout = useCallback(async () => {
trackEvent('Checkout Start', { has_referral: 'false' });
setCheckingOut(true);
setError(null);
try {
const body: Record<string, string> = {};
if (referralCode) body.referral_code = referralCode;
const res = await fetch(apiUrl('checkout'), {
method: 'POST',
...authHeaders({
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
body: JSON.stringify({}),
}),
});
assertOk(res, 'Checkout');

View file

@ -83,7 +83,7 @@ export function useLocationSearch(mode?: string) {
const [activeIndex, setActiveIndex] = useState(-1);
const [open, setOpen] = useState(false);
const abortRef = useRef<AbortController | null>(null);
const debounceRef = useRef<ReturnType<typeof setTimeout>>();
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const latestQueryRef = useRef('');
const lastResultsRef = useRef<SearchResult[]>([]);

View file

@ -6,7 +6,6 @@ import Supercluster from 'supercluster';
import type { POI } from '../types';
import {
POI_GROUP_COLORS,
POI_DEFAULT_COLOR,
MINOR_POI_CATEGORIES,
MINOR_POI_ZOOM_THRESHOLD,
POI_CLUSTER_RADIUS,
@ -40,6 +39,30 @@ interface UsePoiLayersProps {
isDark: boolean;
}
function getPoiIconUrlForPoi(poi: POI): string {
return getPoiIconUrl(poi.category, poi.emoji, poi.icon_category, poi.name);
}
function isBundledPoiIcon(url: string): boolean {
return url.startsWith('/assets/poi-icons/');
}
function hasBundledPoiLogo(poi: POI): boolean {
return isBundledPoiIcon(getPoiIconUrlForPoi(poi));
}
function getPoiGroupColor(group: string): [number, number, number] {
const color = POI_GROUP_COLORS[group];
if (!color) {
throw new Error(`Missing POI group color for '${group}'`);
}
return color;
}
function getPoiIconSize(poi: POI): number {
return hasBundledPoiLogo(poi) ? 24 : 18;
}
export function usePoiLayers({ pois, zoom, isDark }: UsePoiLayersProps) {
const [popupInfo, setPopupInfo] = useState<PopupInfo | null>(null);
@ -139,7 +162,7 @@ export function usePoiLayers({ pois, zoom, isDark }: UsePoiLayersProps) {
id: 'poi-shadow',
data: visiblePois,
getPosition: (d) => [d.lng, d.lat],
getRadius: 16,
getRadius: (d) => (hasBundledPoiLogo(d) ? 0 : 16),
radiusUnits: 'pixels',
getFillColor: isDark ? [0, 0, 0, 50] : [0, 0, 0, 25],
pickable: false,
@ -154,11 +177,17 @@ export function usePoiLayers({ pois, zoom, isDark }: UsePoiLayersProps) {
id: 'poi-background',
data: visiblePois,
getPosition: (d) => [d.lng, d.lat],
getRadius: 14,
getRadius: (d) => (hasBundledPoiLogo(d) ? 24 : 14),
radiusUnits: 'pixels',
getFillColor: isDark ? [41, 37, 36, 255] : [255, 255, 255, 255],
getFillColor: (d) =>
hasBundledPoiLogo(d)
? ([0, 0, 0, 0] as [number, number, number, number])
: isDark
? ([41, 37, 36, 255] as [number, number, number, number])
: ([255, 255, 255, 255] as [number, number, number, number]),
getLineColor: (d) => {
const c = POI_GROUP_COLORS[d.group] || POI_DEFAULT_COLOR;
if (hasBundledPoiLogo(d)) return [0, 0, 0, 0] as [number, number, number, number];
const c = getPoiGroupColor(d.group);
return [c[0], c[1], c[2], 255] as [number, number, number, number];
},
getLineWidth: 2.5,
@ -177,12 +206,16 @@ export function usePoiLayers({ pois, zoom, isDark }: UsePoiLayersProps) {
id: 'poi-icons',
data: visiblePois,
getPosition: (d) => [d.lng, d.lat],
getIcon: (d) => ({
url: getPoiIconUrl(d.category, d.emoji, d.icon_category, d.name),
width: 72,
height: 72,
}),
getSize: 18,
getIcon: (d) => {
const url = getPoiIconUrlForPoi(d);
const isLogo = isBundledPoiIcon(url);
return {
url,
width: isLogo ? 96 : 72,
height: isLogo ? 48 : 72,
};
},
getSize: getPoiIconSize,
sizeUnits: 'pixels',
pickable: false,
transitions: { getSize: { duration: 300, enter: () => [0] } },