Enhance map functionality with bookmark features and UI improvements
- Introduced a new MapBookmarkNameModal for adding and editing bookmarks. - Updated MapView to manage selected markers for bookmarking and handle bookmark name submissions. - Enhanced MapContextMenu with an option to add markers to bookmarks. - Improved MapBookmarks component to support editing bookmark names and adding selected markers. - Refactored MapControls and MapControlsContent to integrate selected marker functionality for bookmarks. - Updated useMapBookmarks composable to include bookmark updating logic. - Removed unused grid coordinates toggle from the UI for a cleaner interface.
This commit is contained in:
@@ -36,26 +36,22 @@ export interface UseMapUpdatesReturn {
|
||||
cleanup: () => void
|
||||
}
|
||||
|
||||
const RECONNECT_INITIAL_MS = 1000
|
||||
const RECONNECT_MAX_MS = 30000
|
||||
|
||||
export function startMapUpdates(options: UseMapUpdatesOptions): UseMapUpdatesReturn {
|
||||
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'
|
||||
}
|
||||
|
||||
const BATCH_MS = 50
|
||||
let batch: TileUpdate[] = []
|
||||
let batchScheduled = false
|
||||
let source: EventSource | null = null
|
||||
let reconnectTimeoutId: ReturnType<typeof setTimeout> | null = null
|
||||
let reconnectDelayMs = RECONNECT_INITIAL_MS
|
||||
let destroyed = false
|
||||
|
||||
const VISIBLE_TILE_BUFFER = 1
|
||||
|
||||
@@ -101,42 +97,74 @@ export function startMapUpdates(options: UseMapUpdatesOptions): UseMapUpdatesRet
|
||||
setTimeout(applyBatch, BATCH_MS)
|
||||
}
|
||||
|
||||
source.onmessage = (event: MessageEvent) => {
|
||||
if (connectionStateRef) connectionStateRef.value = 'open'
|
||||
try {
|
||||
const raw: unknown = event?.data
|
||||
if (raw == null || typeof raw !== 'string' || raw.trim() === '') return
|
||||
const updates: unknown = JSON.parse(raw)
|
||||
if (!Array.isArray(updates)) return
|
||||
for (const u of updates as TileUpdate[]) {
|
||||
batch.push(u)
|
||||
}
|
||||
scheduleBatch()
|
||||
} catch {
|
||||
// Ignore parse errors from SSE
|
||||
function connect() {
|
||||
if (destroyed || !import.meta.client) return
|
||||
source = new EventSource(updatesUrl)
|
||||
if (connectionStateRef) connectionStateRef.value = 'connecting'
|
||||
|
||||
source.onopen = () => {
|
||||
if (connectionStateRef) connectionStateRef.value = 'open'
|
||||
reconnectDelayMs = RECONNECT_INITIAL_MS
|
||||
}
|
||||
|
||||
source.onerror = () => {
|
||||
if (destroyed || !source) return
|
||||
if (connectionStateRef) connectionStateRef.value = 'error'
|
||||
source.close()
|
||||
source = null
|
||||
if (destroyed) return
|
||||
reconnectTimeoutId = setTimeout(() => {
|
||||
reconnectTimeoutId = null
|
||||
connect()
|
||||
reconnectDelayMs = Math.min(reconnectDelayMs * 2, RECONNECT_MAX_MS)
|
||||
}, reconnectDelayMs)
|
||||
}
|
||||
|
||||
source.onmessage = (event: MessageEvent) => {
|
||||
if (connectionStateRef) connectionStateRef.value = 'open'
|
||||
try {
|
||||
const raw: unknown = event?.data
|
||||
if (raw == null || typeof raw !== 'string' || raw.trim() === '') return
|
||||
const updates: unknown = JSON.parse(raw)
|
||||
if (!Array.isArray(updates)) return
|
||||
for (const u of updates as TileUpdate[]) {
|
||||
batch.push(u)
|
||||
}
|
||||
scheduleBatch()
|
||||
} catch {
|
||||
// Ignore parse errors from SSE
|
||||
}
|
||||
}
|
||||
|
||||
source.addEventListener('merge', (e: MessageEvent) => {
|
||||
try {
|
||||
const merge: MergeEvent = JSON.parse((e?.data as string) ?? '{}')
|
||||
if (getCurrentMapId() === merge.From) {
|
||||
const point = map.project(map.getCenter(), 6)
|
||||
const shift = {
|
||||
x: Math.floor(point.x / TileSize) + merge.Shift.x,
|
||||
y: Math.floor(point.y / TileSize) + merge.Shift.y,
|
||||
}
|
||||
onMerge(merge.To, shift)
|
||||
}
|
||||
} catch {
|
||||
// Ignore merge parse errors
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
source.onerror = () => {}
|
||||
|
||||
source.addEventListener('merge', (e: MessageEvent) => {
|
||||
try {
|
||||
const merge: MergeEvent = JSON.parse((e?.data as string) ?? '{}')
|
||||
if (getCurrentMapId() === merge.From) {
|
||||
const point = map.project(map.getCenter(), 6)
|
||||
const shift = {
|
||||
x: Math.floor(point.x / TileSize) + merge.Shift.x,
|
||||
y: Math.floor(point.y / TileSize) + merge.Shift.y,
|
||||
}
|
||||
onMerge(merge.To, shift)
|
||||
}
|
||||
} catch {
|
||||
// Ignore merge parse errors
|
||||
}
|
||||
})
|
||||
connect()
|
||||
|
||||
function cleanup() {
|
||||
source.close()
|
||||
destroyed = true
|
||||
if (reconnectTimeoutId != null) {
|
||||
clearTimeout(reconnectTimeoutId)
|
||||
reconnectTimeoutId = null
|
||||
}
|
||||
if (source) {
|
||||
source.close()
|
||||
source = null
|
||||
}
|
||||
}
|
||||
|
||||
return { cleanup }
|
||||
|
||||
Reference in New Issue
Block a user