From bbf6fbdd13c402c5c197a05489268dc503d12140 Mon Sep 17 00:00:00 2001 From: Pepe Ziberi Date: Wed, 22 Jul 2026 19:46:43 +0200 Subject: [PATCH] =?UTF-8?q?feat(field):=20Symbole=20am=20St=C3=BCck=20setz?= =?UTF-8?q?en=20+=20eigene=20GPS-Position=20(v1.5.4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Tap-to-place bleibt scharf: mehrere gleiche Symbole nacheinander setzen (z.B. 4 Hydranten), Hinweis zeigt Anzahl + "Fertig"-Knopf - Zeichen-Werkzeug wählen beendet den Platzier-Modus - GPS: eigene Position dauerhaft mit Genauigkeitskreis + Blickrichtung - Beim Öffnen bleibt die Karte auf dem Einsatzort (springt nicht mehr auf die eigene Position, wenn ein Einsatzort gesetzt ist) Co-Authored-By: Claude Opus 4.8 (1M context) --- package.json | 2 +- src/app/app/page.tsx | 32 +++++++++++++++++++++++--------- src/components/map/map-view.tsx | 28 +++++++++++++++++++++++----- 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index afdc0ee..4d84253 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lageplan", - "version": "1.5.3", + "version": "1.5.4", "description": "Feuerwehr Lageplan - Krokier-App für Einsatzdokumentation", "private": true, "scripts": { diff --git a/src/app/app/page.tsx b/src/app/app/page.tsx index 55d61ee..9eb5153 100644 --- a/src/app/app/page.tsx +++ b/src/app/app/page.tsx @@ -19,7 +19,7 @@ import { useAuth } from '@/components/providers/auth-provider' import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog' import { JournalView } from '@/components/journal/journal-view' -import { Lock, Eye, AlertTriangle, WifiOff, GraduationCap, Map as MapIcon, ClipboardList, LayoutGrid, X } from 'lucide-react' +import { Lock, Eye, AlertTriangle, WifiOff, GraduationCap, Map as MapIcon, ClipboardList, LayoutGrid } from 'lucide-react' import { CustomDragLayer } from '@/components/map/custom-drag-layer' import { OnboardingTour, resetOnboardingTour } from '@/components/onboarding/onboarding-tour' import { useKeyboardShortcuts } from '@/hooks/use-keyboard-shortcuts' @@ -51,6 +51,8 @@ export default function AppPage() { const [isEndConfirmOpen, setIsEndConfirmOpen] = useState(false) // Tap-to-place: angetipptes Symbol wartet auf Platzierung per Karten-Tipp (Mobil) const [pendingSymbol, setPendingSymbol] = useState<{ id: string; imageUrl: string } | null>(null) + // Feld-Tempo: bleibt scharf, um mehrere gleiche Symbole am Stück zu setzen + const [placedCount, setPlacedCount] = useState(0) const [isSaving, setIsSaving] = useState(false) const [isDeleteAllConfirmOpen, setIsDeleteAllConfirmOpen] = useState(false) const [isFullscreen, setIsFullscreen] = useState(false) @@ -403,7 +405,11 @@ export default function AppPage() { // Werkzeugwechsel auf ein Zeichen-Tool übernimmt vorab den Lock (rechtzeitig vor dem Zeichnen) const handleDrawModeChange = useCallback((mode: DrawMode) => { - if (mode !== 'select') ensureEditing() + if (mode !== 'select') { + ensureEditing() + setPendingSymbol(null) // ein Zeichen-Werkzeug beendet das Symbol-Platzieren + setPlacedCount(0) + } setDrawMode(mode) }, [ensureEditing, setDrawMode]) @@ -769,10 +775,11 @@ export default function AppPage() { const handleSymbolSelect = useCallback((symbol: { id: string; imageUrl: string }) => { ensureEditing() // Lock schon beim Auswählen übernehmen, damit das Platzieren sofort greift setPendingSymbol(symbol) + setPlacedCount(0) setIsSidebarOpen(false) setActiveTab('map') setDrawMode('select') - toast({ title: 'Symbol platzieren', description: 'Tippe auf die Karte, um das Symbol zu setzen.' }) + toast({ title: 'Symbol platzieren', description: 'Auf die Karte tippen — mehrere möglich, dann „Fertig".' }) }, [setIsSidebarOpen, setActiveTab, toast, ensureEditing, setDrawMode]) const handleTextPlace = useCallback((coordinates: [number, number]) => { @@ -1038,7 +1045,7 @@ export default function AppPage() { onDrawModeChange={setDrawMode} undoDrawPointRef={undoDrawPointRef} pendingSymbol={pendingSymbol ? { iconId: pendingSymbol.id, imageUrl: pendingSymbol.imageUrl } : null} - onSymbolPlaced={() => setPendingSymbol(null)} + onSymbolPlaced={() => setPlacedCount((n) => n + 1)} isFullscreen={isFullscreen} onToggleFullscreen={toggleFullscreen} /> @@ -1093,13 +1100,20 @@ export default function AppPage() { /> - {/* Tap-to-place Hinweis — schwebt über der Bottom-Nav, mit Abbrechen */} + {/* Tap-to-place Hinweis — schwebt über der Bottom-Nav; mehrere setzen, dann „Fertig" */} {pendingSymbol && ( -
+
- Auf die Karte tippen zum Platzieren -
)} diff --git a/src/components/map/map-view.tsx b/src/components/map/map-view.tsx index a97234e..18a491b 100644 --- a/src/components/map/map-view.tsx +++ b/src/components/map/map-view.tsx @@ -706,16 +706,34 @@ export function MapView({ map.current.addControl(new maplibregl.NavigationControl(), 'bottom-right') map.current.addControl(new maplibregl.ScaleControl(), 'bottom-left') - // Geolocation: center map on user's position + // Geolocation: eigene Position dauerhaft anzeigen (Punkt + Genauigkeit + Blickrichtung) const geolocate = new maplibregl.GeolocateControl({ positionOptions: { enableHighAccuracy: true }, trackUserLocation: true, - showAccuracyCircle: false, - }) + showAccuracyCircle: true, + // showUserHeading zeigt die Blickrichtung — in den Typen dieser Version (noch) + // nicht deklariert, zur Laufzeit aber vorhanden. + showUserHeading: true, + } as any) map.current.addControl(geolocate, 'bottom-right') - // Auto-trigger geolocation when map loads to center on user's real position - map.current.on('load', () => { geolocate.trigger() }) + // Position beim Laden aktivieren (Punkt erscheint), aber bei gesetztem Einsatzort + // auf dem Einsatzort bleiben statt auf die eigene Position zu springen. + const einsatzortCenter = project?.mapCenter + const hasEinsatzort = !!einsatzortCenter && + !(einsatzortCenter.lng === 8.5417 && einsatzortCenter.lat === 47.3769) + map.current.on('load', () => { + geolocate.trigger() + if (hasEinsatzort && einsatzortCenter) { + geolocate.once('geolocate', () => { + map.current?.easeTo({ + center: [einsatzortCenter.lng, einsatzortCenter.lat], + zoom: project?.mapZoom || 17, + duration: 600, + }) + }) + } + }) map.current.on('load', () => { const m = map.current