Implement error handling and visual enhancements in map components

- Introduced MapErrorBoundary component to handle map loading errors gracefully.
- Enhanced MapView with a reconnection status indicator for live updates.
- Added tile freshness animation to indicate updated tiles visually.
- Preloaded marker icon images to improve rendering performance.
- Updated various pages to utilize the new MapErrorBoundary for better user experience.
This commit is contained in:
2026-03-01 16:04:19 +03:00
parent 7f990c0c11
commit db0b48774a
9 changed files with 128 additions and 11 deletions

View File

@@ -1,9 +1,12 @@
import type { Ref } from 'vue'
import type { SmartTileLayer } from '~/lib/SmartTileLayer'
import { TileSize } from '~/lib/LeafletCustomTypes'
import type L from 'leaflet'
type SmartTileLayerInstance = InstanceType<typeof SmartTileLayer>
export type SseConnectionState = 'connecting' | 'open' | 'error'
interface TileUpdate {
M: number
X: number
@@ -25,6 +28,8 @@ export interface UseMapUpdatesOptions {
map: L.Map
getCurrentMapId: () => number
onMerge: (mapTo: number, shift: { x: number; y: number }) => void
/** Optional ref updated with SSE connection state for reconnection indicator. */
connectionStateRef?: Ref<SseConnectionState>
}
export interface UseMapUpdatesReturn {
@@ -32,13 +37,24 @@ export interface UseMapUpdatesReturn {
}
export function startMapUpdates(options: UseMapUpdatesOptions): UseMapUpdatesReturn {
const { backendBase, layer, overlayLayer, map, getCurrentMapId, onMerge } = options
const { backendBase, layer, overlayLayer, map, getCurrentMapId, onMerge, connectionStateRef } = options
const updatesPath = `${backendBase}/updates`
const updatesUrl = import.meta.client ? `${window.location.origin}${updatesPath}` : updatesPath
const source = new EventSource(updatesUrl)
if (connectionStateRef) {
connectionStateRef.value = 'connecting'
}
source.onopen = () => {
if (connectionStateRef) connectionStateRef.value = 'open'
}
source.onerror = () => {
if (connectionStateRef) connectionStateRef.value = 'error'
}
source.onmessage = (event: MessageEvent) => {
if (connectionStateRef) connectionStateRef.value = 'open'
try {
const raw: unknown = event?.data
if (raw == null || typeof raw !== 'string' || raw.trim() === '') return