aprs.me/assets/js/map_helpers.ts
Graham McIntire af4ab270e2
Replace any types with specific TypeScript types
- Add comprehensive type definitions for Leaflet plugins (heatmap, marker clustering, overlapping marker spiderfier)
- Create event payload interfaces for all LiveView events
- Add marker extension types for APRS-specific properties
- Define Chart.js dataset and configuration types
- Replace 74 instances of 'any' type across all TypeScript files
- Improve type safety for event handlers and state management
- Add proper typing for external library integrations

This refactoring enhances type safety throughout the codebase, making it easier to catch errors at compile time and improving IDE support for autocompletion and refactoring.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-17 17:55:27 -05:00

120 lines
No EOL
3.2 KiB
TypeScript

// Helper functions for map.ts to reduce code duplication
import type * as L from 'leaflet';
import type { BaseEventPayload, PushEventFunction } from './types/events';
import type { LiveSocket } from './types/map';
export interface MapState {
lat: number;
lng: number;
zoom: number;
}
export interface BoundsData {
north: number;
south: number;
east: number;
west: number;
}
/**
* Parse timestamp to milliseconds
*/
export function parseTimestamp(timestamp: string | number | Date | undefined): number {
if (!timestamp) return Date.now();
if (typeof timestamp === "number") {
return timestamp;
} else if (typeof timestamp === "string") {
return new Date(timestamp).getTime();
}
return Date.now();
}
/**
* Get trail ID from marker data
*/
export function getTrailId(data: { callsign_group?: string; callsign?: string; id: string }): string {
return data.callsign_group || data.callsign || data.id;
}
/**
* Save map state to localStorage and send to server
*/
// PushEventFunction is now imported from types/events
export function saveMapState(map: L.Map, pushEvent: PushEventFunction) {
if (!map || !pushEvent) {
console.warn("saveMapState called with invalid map or pushEvent");
return;
}
try {
const center = map.getCenter();
const zoom = map.getZoom();
// Truncate lat/lng to 5 decimal places for URL
const truncatedLat = Math.round(center.lat * 100000) / 100000;
const truncatedLng = Math.round(center.lng * 100000) / 100000;
// Always save to localStorage, even if LiveView is disconnected
// Temporarily disabled - using server-side geolocation instead
/*
localStorage.setItem(
"aprs_map_state",
JSON.stringify({ lat: truncatedLat, lng: truncatedLng, zoom }),
);
*/
// Send combined map state update to server for URL and bounds updating
const payload = {
center: { lat: truncatedLat, lng: truncatedLng },
zoom: zoom,
bounds: {
north: map.getBounds().getNorth(),
south: map.getBounds().getSouth(),
east: map.getBounds().getEast(),
west: map.getBounds().getWest(),
}
};
console.debug("Sending update_map_state event:", payload);
// Use safePushEvent to handle disconnected state
safePushEvent(pushEvent, "update_map_state", payload);
} catch (error) {
console.error("Error in saveMapState:", error);
}
}
/**
* Safely push event to LiveView
*/
export function safePushEvent<T extends BaseEventPayload = BaseEventPayload>(pushEvent: PushEventFunction | undefined, event: string, payload: T): boolean {
if (!pushEvent || typeof pushEvent !== 'function') {
console.debug(`pushEvent not available for ${event} event`);
return false;
}
try {
pushEvent(event, payload);
return true;
} catch (e) {
console.debug(`Unable to send ${event} event:`, e);
return false;
}
}
/**
* Check if LiveView socket is available
*/
export function isLiveViewConnected(): boolean {
return typeof window !== 'undefined' && !!window.liveSocket;
}
/**
* Get LiveView socket
*/
export function getLiveSocket(): LiveSocket | null {
return typeof window !== 'undefined' ? window.liveSocket || null : null;
}