- 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.
7.7 KiB
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 →MainTabsmitMainTab(Start, Tagebuch, Mitglieder, …). - Breit / schmal: ab ca. 600 dp
MainNavigationRail, sonstBottomNavigation(sieheMAIN_NAV_RAIL_MIN_WIDTH_DP). - Unter-Navigation / Tiefe: kein zentraler Navigation-Compose-
NavHostfür die Shell; stattdessen expliziter Zustand (rememberSaveable/remember), z. B.diarySelectedEntryId,membersNestedOpen,billingOrdersSection. Zurück überBackHandlerund 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 (visibleMainTabsinAppRoot.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 untercomposeApp/.../ui/mit dateiweise Feature-Namen (Diary*,Members*,Schedule*,CalendarScreen, …) sowieAppRoot.kt,AppDependencies.kt.- Zielbild: Neue Features als eigene Dateien/ Unterpakete
ui/<bereich>/anlegen; keine weiteren riesigen Monolithen nebenAppRootanhä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 incomposeApp(z. B.calendar/CalendarAggregator.kt), angebunden überAppDependencies. - 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:
LaunchedEffectbei 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 imshared(Dependency existiert bereits) anbinden oder dokumentiertes Polling – sieheTODO.mdBacklog.
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 (
sharePdfFileu. 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
-
Android Studio überschreibt Gradle:
Settings → Build, Execution, Deployment → Build Tools → Gradle
beim Projektmobile-appprüfen, ob unter Command-line options etwas wie
-PbackendBaseUrl=http://localhost:3005steht – das entfernen oder bewusst setzen. -
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-cacheOder das Skript:
./scripts/install-debug-emulator.sh -
BuildConfig prüfen: Nach dem Build in
composeApp/build/generated/.../BuildConfig.java
den Wert vonBACKEND_BASE_URLansehen – der musshttps://tt-tagebuch.desein (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
composeAppwä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)
- Start backend (default
http://localhost:3005) - Start Android emulator
- App installieren:
./scripts/install-debug-emulator.shoder Run in Studio (ModulcomposeApp) - Login → Club auswählen → Rolle wird angezeigt
- Open
Tagebuch→ die letzten Einträge werden angezeigt
Phase 14 – Unit-Tests, Regressionsliste, R8
- Automatisierte Tests (shared):
./gradlew :shared:testDebugUnitTest(JVM-Unit-Tests untershared/src/androidUnitTest/, u. a. Serialisierung kritischer DTOs). - Manuelle Regression:
REGRESSION_CHECKLIST.mdim Ordnermobile-app/. - R8/ProGuard:
composeApp/proguard-rules.proist an die Release-Variante angebunden;isMinifyEnabledbleibt vorerstfalse, 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 thecomposeAppconfiguration.