Session 2026-04-19: Navigation, Kompass, Übungsfortschritt
Routen-Navigation:
- POI-Marker: farbige Kreise mit Phosphor-Icons (wie Hauptkarte)
- Screensaver: Navi-Pfeil dreht sich via DeviceOrientationEvent (iOS+Android)
- Pfeil-Dämpfung: EMA α=0.12 mit Wrap-Around
- GPS-Distanz-Bug: Fortschritt nur wenn <500m zur Route
- fitBounds: User-Position nur wenn <20km von Route
- Screensaver: "zur Route" vs "verbleibend" kontextabhängig
- Richtungspfeile entlang Route (blau, max 7 Stück)
- Umkehren ins Route-Detail verschoben, Detail-Map rebuildet sich
- rk-header z-index:10 (Leaflet-Tiles liefen drüber)
- 2-Sek. Screensaver-Entsperrung
km-Tracking:
- route_walks Tabelle
- POST /api/routes/{id}/walked (≥50%)
- total_km = erstellte Routes + gelaufene route_walks
- Toast bei neuem Badge
Übungsfortschritt:
- exercise_progress + training_plan_progress Tabellen
- GET/POST /api/training/progress, /plan-progress, /suggestions
- uebungen.js: API-first + localStorage-Fallback + Auto-Migration
- Empfehlungs-Banner (regelbasiert)
- Toast bei "sitzt"
This commit is contained in:
parent
390176383f
commit
9a78121a3e
25 changed files with 2487 additions and 248 deletions
|
|
@ -68,9 +68,14 @@ class ResolveReport(BaseModel):
|
|||
# ------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ------------------------------------------------------------------
|
||||
_FORUM_ALLOWED_EXT = {".jpg",".jpeg",".png",".gif",".webp",".heic",".heif",
|
||||
".mp4",".mov",".webm",".m4v",".pdf",".avi"}
|
||||
|
||||
def _save_upload(file: UploadFile, data: bytes) -> str:
|
||||
os.makedirs(FORUM_DIR, exist_ok=True)
|
||||
ext = os.path.splitext(file.filename or "")[1] or ".jpg"
|
||||
ext = os.path.splitext(file.filename or "")[1].lower() or ".jpg"
|
||||
if ext not in _FORUM_ALLOWED_EXT:
|
||||
raise HTTPException(415, "Dateityp nicht erlaubt.")
|
||||
filename = f"{uuid.uuid4().hex}{ext}"
|
||||
path = os.path.join(FORUM_DIR, filename)
|
||||
with open(path, "wb") as f:
|
||||
|
|
@ -573,7 +578,7 @@ async def members_map():
|
|||
AND forum_lat IS NOT NULL
|
||||
AND forum_lon IS NOT NULL"""
|
||||
).fetchall()
|
||||
return [{'vorname': r['vorname'] or '?', 'lat': round(r['lat'], 2), 'lon': round(r['lon'], 2)}
|
||||
return [{'vorname': r['vorname'] or '?', 'lat': round(r['lat'], 3), 'lon': round(r['lon'], 3)}
|
||||
for r in rows]
|
||||
|
||||
|
||||
|
|
@ -583,16 +588,13 @@ async def members_map():
|
|||
@router.patch("/members/location")
|
||||
async def set_member_location(data: LocationBody, user=Depends(get_current_user)):
|
||||
if data.show and data.lat is not None and data.lon is not None:
|
||||
# Snap to ~1km grid (2 decimal places ≈ 1.1km)
|
||||
snapped_lat = round(data.lat, 2)
|
||||
snapped_lon = round(data.lon, 2)
|
||||
with db() as conn:
|
||||
conn.execute(
|
||||
"""UPDATE users SET forum_lat=?, forum_lon=?, forum_show_location=1
|
||||
WHERE id=?""",
|
||||
(snapped_lat, snapped_lon, user['id'])
|
||||
(round(data.lat, 4), round(data.lon, 4), user['id'])
|
||||
)
|
||||
return {"ok": True, "lat": snapped_lat, "lon": snapped_lon}
|
||||
return {"ok": True, "lat": data.lat, "lon": data.lon}
|
||||
else:
|
||||
with db() as conn:
|
||||
conn.execute(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue