Files
trainingstagebuch/mobile-app/DEVELOPMENT.md
Torsten Schulz (local) 83294406a4
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 44s
feat(Diary): implement quick create functionality for training days and enhance localization
- Added a new button for quick creation of training days in the DiaryView, improving user experience.
- Implemented logic to find the next available training slot across groups and create a training day entry.
- Enhanced localization by adding new keys for quick create messages in multiple languages, ensuring better accessibility for users.
- Updated the DiaryManager to handle quick create operations and clear errors effectively.
2026-05-14 22:35:29 +02:00

7.7 KiB
Raw Permalink Blame History

Development

Architektur Phase 0 (abgeschlossen)

Die folgenden Punkte entsprechen Phase 0 in TODO.md: festgelegte Entscheidungen und Ist-Zustand der Codebasis, ohne dass die App komplett neu strukturiert wird.

Navigation

  • Single Activity, Haupt-UI in AppRoot.kt: eingeloggt → MainTabs mit MainTab (Start, Tagebuch, Mitglieder, …).
  • Breit / schmal: ab ca. 600dp MainNavigationRail, sonst BottomNavigation (siehe MAIN_NAV_RAIL_MIN_WIDTH_DP).
  • Unter-Navigation / Tiefe: kein zentraler Navigation-Compose-NavHost für die Shell; stattdessen expliziter Zustand (rememberSaveable / remember), z.B. diarySelectedEntryId, membersNestedOpen, billingOrdersSection. Zurück über BackHandler und Tab-Wechsel, der Detailzustand zurücksetzt (selectMainTab).
  • Auth: eigener Flow (AuthFlowHost) vor Club-Auswahl ebenfalls ohne separates Nav-Graph-Modul.
  • Begründung (kein zentraler NavHost): geringere Migrationskosten, vorhandenes Verhalten beibehalten. Ein späterer Umstieg auf NavHost (z.B. pro Tab oder für Deep Links) ist möglich.
  • Start-Hub (HomeScreen): Nur Willkommen + Vereinsinfos (Karte); keine Navigations-Kacheln mehr (vermeidet leere Abschnitte und Doppelung mit der Rail).
  • Navigation Rail (Tablet / breit): Drei aufklappbare Blöcke wie die Web-Sidebar Tagesgeschäft, Wettbewerb, Einstellungen mit allen Einträgen inkl. Freigaben, Turnierteilnahmen (Browser), Vereinseinstellungen, Vordefinierte Aktivitäten, Team (Browser), Abrechnung, Bestellungen; oben Start, unten Mehr. Zustand der Aufklappung per rememberSaveable.
  • Smartphone: Weiterhin untere Tab-Leiste (visibleMainTabs) + vollständige Einstellungen unter Mehr; Kurzhinweis auf der Startseite.
  • Tab-Sichtbarkeit: Entspricht grob den Web-v-if-Bedingungen (visibleMainTabs in AppRoot.kt, u.a. Kalender nur bei Tagebuch-, Terminplan- oder Turnier-Leserecht).

Feature-Paketierung (Richtlinie + Ist)

  • shared: HTTP-APIs (shared/.../api), DTOs (api/models), zustandsführende Manager (shared/.../state), i18n.
  • composeApp: Android-UI unter composeApp/.../ui/ mit dateiweise Feature-Namen (Diary*, Members*, Schedule*, CalendarScreen, …) sowie AppRoot.kt, AppDependencies.kt.
  • Zielbild: Neue Features als eigene Dateien/ Unterpakete ui/<bereich>/ anlegen; keine weiteren riesigen Monolithen neben AppRoot anhäufen, sondern schrittweise auslagern (laufende Arbeit).

Use-Cases / Geschäftslogik

  • Primär shared: *Manager-Klassen kapseln API + Caching/Flow (z.B. DiaryManager, ClubManager).
  • Plattformnah Android: reine UI-Hilfen oder java.time-lastige Aggregation bleiben in composeApp (z.B. calendar/CalendarAggregator.kt), angebunden über AppDependencies.
  • Composables: möglichst nur Zustand, LaunchedEffect, Aufrufe in Manager/APIs; komplexe Ableitungen in wiederverwendbaren Funktionen/Klassen statt inline riesiger Blöcke.

Echtzeit (optional / Web-Parität)

  • Web nutzt u.a. Socket-Updates; Android v1: bewusst ohne parallelen Socket-Client.
  • Aktualisierung: LaunchedEffect bei Screen-Fokus / clubId / relevanten Keys, nach Schreiboperationen (dataGeneration++ o. ä.) oder Tabwechsel. Polling nur dort, wo bereits im Code nachvollziehbar (kein globales Intervall-Polling).
  • Später: optional socket.io-Client im shared (Dependency existiert bereits) anbinden oder dokumentiertes Polling siehe TODO.md Backlog.

Medien (Bilder / PDF)

  • Bilder: Coil (coil-compose), authentifizierte Requests wo nötig (diaryAuthHeaders / AuthenticatedAsyncImage).
  • PDF / Teilen: Generierung im App-Code, Ausgabe über FileProvider und System-Intent (sharePdfFile u.a. im Tagebuch-Flow).
  • Cache: Standard Coil-/Systemverhalten; kein separates Offline-Framework in Phase 0.

Backend-URL

Standard: https://tt-tagebuch.de gesetzt in gradle.properties (backendBaseUrl=…) und als Fallback in composeApp/build.gradle.kts.

Für lokale Entwicklung läuft der Backend-Server auf deinem Rechner typischerweise unter http://localhost:3005.

Die Android-Studio-VM (Emulator) kann localhost / 127.0.0.1 nicht für diesen lokalen Server nutzen: dort ist „localhost“ der Emulator selbst, nicht dein PC. Für lokale Emulator-Tests nutze http://10.0.2.2:3005.

HTTP / Cleartext: Ab Android 9 blockiert das System unverschlüsseltes HTTP standardmäßig. Für die lokale Dev-URL ist das in composeApp/src/androidMain/res/xml/network_security_config.xml für 10.0.2.2, localhost und 127.0.0.1 freigegeben.
Testest du mit der LAN-IP deines PCs (echtes Gerät), musst du diese IP dort als weiteres <domain> ergänzen oder kurzzeitig HTTPS nutzen.

Wenn die App trotz Rebuild noch localhost zeigt

  1. Android Studio überschreibt Gradle:
    Settings → Build, Execution, Deployment → Build Tools → Gradle
    beim Projekt mobile-app prüfen, ob unter Command-line options etwas wie
    -PbackendBaseUrl=http://localhost:3005 steht das entfernen oder bewusst setzen.

  2. Alte Installation / Build-Cache: Emulator-App deinstallieren, Clean, neu installieren:

    cd mobile-app
    adb uninstall de.tsschulz.tt_tagebuch
    ./gradlew :composeApp:clean :composeApp:installDebug --no-configuration-cache
    

    Oder das Skript: ./scripts/install-debug-emulator.sh

  3. BuildConfig prüfen: Nach dem Build in
    composeApp/build/generated/.../BuildConfig.java
    den Wert von BACKEND_BASE_URL ansehen der muss https://tt-tagebuch.de sein (oder deine bewusste Override-URL).

Android Studio „Run“ geht nicht

  • Projekt mobile-app/ als Root öffnen (nicht nur den übergeordneten Monorepo-Ordner, wenn Studio das Modul nicht erkennt).
  • Run → Edit Configurations → Android App: Modul composeApp wählen.
  • Alternativ immer über die Kommandozeile installieren (siehe oben) und die App im Emulator starten.

Überschreiben per Gradle-Property, z. B. lokaler Emulator:

./gradlew :composeApp:installDebug -PbackendBaseUrl=http://10.0.2.2:3005

Alternative: Port vom PC auf den Emulator weiterleiten, dann geht wieder localhost:

adb reverse tcp:3005 tcp:3005
./gradlew :composeApp:installDebug -PbackendBaseUrl=http://localhost:3005

Smoke flow (Android)

  1. Start backend (default http://localhost:3005)
  2. Start Android emulator
  3. App installieren: ./scripts/install-debug-emulator.sh oder Run in Studio (Modul composeApp)
  4. Login → Club auswählen → Rolle wird angezeigt
  5. Open Tagebuch → die letzten Einträge werden angezeigt

Phase 14 Unit-Tests, Regressionsliste, R8

  • Automatisierte Tests (shared): ./gradlew :shared:testDebugUnitTest (JVM-Unit-Tests unter shared/src/androidUnitTest/, u.a. Serialisierung kritischer DTOs).
  • Manuelle Regression: REGRESSION_CHECKLIST.md im Ordner mobile-app/.
  • R8/ProGuard: composeApp/proguard-rules.pro ist an die Release-Variante angebunden; isMinifyEnabled bleibt vorerst false, bis ein Release mit Schrumpfung gezielt getestet wurde.

Gradle Wrapper

Use the wrapper from mobile-app/:

cd mobile-app
./gradlew tasks

Note (Codex sandbox only): the sandbox home directory is read-only, so run with:

GRADLE_USER_HOME=/tmp/gradle-home ./gradlew tasks

i18n generation

node scripts/generate-mobile-i18n.js

This reads all web locale JSON files and writes the generated KMP bundle to mobile-app/shared/src/commonMain/kotlin/de/tt_tagebuch/shared/i18n/MobileStrings.kt.

Build

Recommended:

  • Open mobile-app/ in Android Studio and run the composeApp configuration.