28 Commits

Author SHA1 Message Date
Torsten Schulz (local)
645e4b9655 Füge .aab-Dateien zum .gitignore hinzu und aktualisiere die Android-Version auf 0.10.0
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 3m42s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m19s
2026-09-11 15:15:17 +02:00
Torsten Schulz (local)
93b76d4446 Entferne nicht mehr benötigte AAB-Binärdateien für Instant, Local und Production Builds.
Some checks failed
Code Analysis and Production Deploy / deploy-production (push) Has been cancelled
Code Analysis and Production Deploy / deploy-test (push) Has been cancelled
Code Analysis and Production Deploy / analyze (push) Has been cancelled
2026-09-11 15:15:11 +02:00
Torsten Schulz (local)
2842c89bef Füge Unterstützung für QTTR-Listenaktualisierungen hinzu: Implementiere Push-Benachrichtigung für aktualisierte QTTR-Listen und verbessere die Importlogik.
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 4m6s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m41s
2026-09-11 15:07:30 +02:00
Torsten Schulz (local)
dc08ae98ea Füge Unterstützung für die Installation-ID in Push-Token-Registrierung hinzu: Aktualisiere API- und Datenbank-Logik zur Speicherung der Installation-ID.
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 3m36s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m25s
2026-09-09 14:04:41 +02:00
Torsten Schulz (local)
5e8006323b Füge --no-audit und --fund=false zu npm-Befehlen in den Deployment-Skripten hinzu, um potenzielle Blockierungen während der Installation zu vermeiden.
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 4m54s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m56s
2026-09-09 13:24:11 +02:00
Torsten Schulz (local)
e02de6cbb2 Füge Unterstützung für die Registrierung von Geräten hinzu: Implementiere die Registrierung des aktuellen Geräts und verbessere die Fehlerbehandlung für Push-Token.
Some checks failed
Code Analysis and Production Deploy / analyze (push) Failing after 4m38s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Has been skipped
2026-09-09 13:10:22 +02:00
Torsten Schulz (local)
3e981de770 Füge Unterstützung für Push-Benachrichtigungen hinzu: Registriere das aktuelle Gerät bei der Anmeldung und synchronisiere den Push-Token.
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 4m41s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m43s
2026-09-08 16:00:05 +02:00
Torsten Schulz (local)
65e7563e79 Aktualisiere Versionsnummer auf 1.8.9 in package.json
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 4m52s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 3m3s
2026-09-08 14:57:30 +02:00
Torsten Schulz (local)
9bc9501219 Füge Unterstützung für TTR-Änderungen hinzu: Erweitere Benachrichtigungseinstellungen und implementiere Push-Benachrichtigungen für TTR-Wertänderungen.
Some checks failed
Code Analysis and Production Deploy / deploy-production (push) Has been cancelled
Code Analysis and Production Deploy / deploy-test (push) Has been cancelled
Code Analysis and Production Deploy / analyze (push) Has been cancelled
2026-09-08 14:54:20 +02:00
Torsten Schulz (local)
5ca4253b44 Verbessere Geschlechtsnormalisierung: Füge Unterstützung für englische Begriffe hinzu und verbessere die Zuordnung von Geschlechtsdaten im Importprozess.
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 4m25s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m33s
2026-09-04 23:12:06 +02:00
Torsten Schulz (local)
eb75f86d45 Verbessere QTTR-Handling: Aktualisiere die Anzeige von QTTR-Werten zu TTR- und QTTR-Werten und passe die zugehörigen Texte an.
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 4m25s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m31s
2026-09-04 16:58:17 +02:00
Torsten Schulz (local)
395e2a84bd Verbessere TTR-Wert-Import: Füge parallele Abrufe für aktuelle und frühere Ranglisten hinzu und verbessere die Zuordnung von QTTR-Daten.
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 4m27s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m38s
2026-09-04 16:39:34 +02:00
Torsten Schulz (local)
3fb60227df Verbessere Login-Handling: Füge Unterstützung für das automatische Hinzufügen des Intent-Feldes im Login-Formular hinzu und verbessere die Cookie-Verwaltung bei der Anmeldung.
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 4m34s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m40s
2026-09-04 16:25:08 +02:00
Torsten Schulz (local)
9a4eb77c56 Verbessere CAPTCHA-Handling im Login-Prozess: Füge detaillierte Protokollierung des CAPTCHA-Status hinzu und verbessere die Handhabung des CAPTCHA-Widgets.
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 4m38s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m42s
2026-09-04 16:07:18 +02:00
Torsten Schulz (local)
02c1765214 Füge Unterstützung für das Akzeptieren von Cookie-Zustimmungen hinzu: Implementiere eine Funktion, die verschiedene Consent-Dialoge automatisch akzeptiert.
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 4m50s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m42s
2026-09-04 15:43:11 +02:00
Torsten Schulz (local)
ee93e15af8 Verbessere CAPTCHA-Handling im Login-Prozess: Implementiere automatische Token-Generierung und verbessere Fehlerbehandlung.
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 4m55s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m45s
2026-09-04 15:11:15 +02:00
Torsten Schulz (local)
d31026a606 Verbessere Fehlerbehandlung beim Import von TTR-Werten: Kürze Fehlermeldungen im CMS und schreibe vollständige Details ins Serverlog.
Some checks failed
Code Analysis and Production Deploy / analyze (push) Successful in 4m51s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Has been cancelled
2026-09-04 15:05:12 +02:00
Torsten Schulz (local)
4dca2af645 Füge Playwright-Installation hinzu: Überprüfe und installiere den Chromium-Browser in den Deployment-Skripten
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 4m31s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 3m6s
2026-09-04 14:46:14 +02:00
Torsten Schulz (local)
63b5786e43 Füge myTischtennis-Verbindung hinzu: Ergänze Navigationslinks, verbessere Fehlerbehandlung und aktualisiere Import-Logik
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 4m36s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m33s
2026-09-04 14:24:11 +02:00
Torsten Schulz (local)
497d804244 Verbessere das Layout des Navigationsmenüs: Füge Flexbox-Klassen für das 'intern'-Submenü hinzu und optimiere die Darstellung der Trennlinien.
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 4m39s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m38s
2026-09-04 13:53:39 +02:00
Torsten Schulz (local)
9ca1464899 Aktualisiere qs auf Version 6.16.0 und passe den OSV-Scanner-Pfad an
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 12m33s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 3m5s
2026-09-04 13:06:32 +02:00
Torsten Schulz (local)
8f6fda2e61 Füge myTischtennis-Verbindung hinzu: Implementiere Authentifizierung, Import von TTR-Werten und speichere Verbindungsdaten sicher
Some checks failed
Code Analysis and Production Deploy / analyze (push) Failing after 8m54s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Has been skipped
2026-09-04 12:45:22 +02:00
Torsten Schulz (local)
3314aa06b4 Aktualisiere Footer-Stile und verbessere Zugänglichkeit; erhöhe die Version auf 1.8.8
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 6m7s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 3m13s
2026-08-27 13:03:44 +02:00
Torsten Schulz (local)
12c909d6a4 Füge Sparse-Checkout für den Web-Workflow hinzu und ignoriere JVM Heap-Dumps
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 5m37s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m53s
2026-08-26 14:09:52 +02:00
Torsten Schulz (local)
c2594fd330 Entferne nicht mehr benötigte HPROF-Datei zur Optimierung der App-Größe
Some checks failed
Code Analysis and Production Deploy / analyze (push) Has started running
Code Analysis and Production Deploy / deploy-production (push) Has been cancelled
Code Analysis and Production Deploy / deploy-test (push) Has been cancelled
2026-08-26 14:09:40 +02:00
Torsten Schulz (local)
00b08e2f92 Füge Scrollsteuerung für Mannschaften-Untermenü hinzu und optimiere Importpfade
Some checks failed
Code Analysis and Production Deploy / analyze (push) Failing after 2m47s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Has been skipped
2026-08-26 13:51:07 +02:00
Torsten Schulz (local)
93da9fbcbe Füge Scrollfunktion für Mannschaften-Untermenü hinzu und optimiere Button-Interaktion
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 9m39s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m56s
2026-08-26 12:49:07 +02:00
Torsten Schulz (local)
82dcb1da40 Update Navigation component styles and bump version to 1.8.7
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 10m21s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 3m4s
2026-08-26 11:54:48 +02:00
44 changed files with 1073 additions and 268 deletions

View File

@@ -16,6 +16,13 @@ jobs:
# Der Analyse-Workflow benötigt keine Historie. Ein flacher Checkout
# verhindert, dass der Runner das große Repository-Pack vollständig lädt.
fetch-depth: 1
# Der Web-Workflow braucht keine Android-Artefakte. Insbesondere liegt
# dort ein großer Heap-Dump im aktuellen Commit, der auch bei einem
# flachen Checkout übertragen würde und den Runner-Checkout abbricht.
sparse-checkout: |
/*
!/android-app/
sparse-checkout-cone-mode: false
- name: Ensure clean workspace
run: |
@@ -114,11 +121,12 @@ jobs:
- name: OSV-Scanner (SCA)
run: |
cd "$GITHUB_WORKSPACE"
curl -L -o osv-scanner https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_amd64
chmod +x osv-scanner
./osv-scanner --version
test -f ./package-lock.json
./osv-scanner scan -L ./package-lock.json --config ./.osv-scanner.toml
test -f "$GITHUB_WORKSPACE/package-lock.json"
./osv-scanner scan -L "$GITHUB_WORKSPACE/package-lock.json" --config "$GITHUB_WORKSPACE/.osv-scanner.toml"
deploy-production:
runs-on: ubuntu-latest

4
.gitignore vendored
View File

@@ -94,6 +94,10 @@ dist
/android-app/**/build/
/android-app/local.properties
/android-app/gradle-local.properties
# Android Play Store / release artifacts are generated locally or in CI.
/android-app/**/*.aab
# JVM Heap-Dumps sind lokale Diagnoseartefakte und dürfen nie ins Repository.
*.hprof
# Build output (but keep production data!)
.output

View File

@@ -21,9 +21,20 @@ import de.harheimertc.ui.navigation.NavigationViewModel
import dagger.hilt.android.AndroidEntryPoint
import android.util.Log
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.lifecycleScope
import de.harheimertc.repositories.AuthRepository
import de.harheimertc.repositories.PushTokenRepository
import kotlinx.coroutines.launch
import javax.inject.Inject
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@Inject
lateinit var authRepository: AuthRepository
@Inject
lateinit var pushTokenRepository: PushTokenRepository
private val notificationRoute = mutableStateOf<String?>(null)
private val notificationPermissionLauncher = registerForActivityResult(
@@ -50,6 +61,23 @@ class MainActivity : ComponentActivity() {
notificationRoute.value = extractNotificationRoute(intent)
}
override fun onResume() {
super.onResume()
syncPushTokenWhenSignedIn()
}
/**
* Server-side tokens are persistent, but re-registering the current FCM
* token is cheap and idempotent. It restores a token if server data was
* ever recovered from a backup or manually repaired.
*/
private fun syncPushTokenWhenSignedIn() {
if (authRepository.getToken().isNullOrBlank() && authRepository.getRefreshToken().isNullOrBlank()) return
lifecycleScope.launch {
pushTokenRepository.registerCurrentDevice()
}
}
private fun extractNotificationRoute(intent: Intent?): String? =
intent?.getStringExtra(EXTRA_NOTIFICATION_ROUTE)?.takeIf { it.isNotBlank() }

View File

@@ -269,6 +269,7 @@ data class NotificationSettingsDto(
val birthdays: Boolean = false,
val newContactRequest: Boolean = false,
val newUserRegistration: Boolean = false,
val ownTtrChanges: Boolean = false,
val selectedTeamSlugs: List<String> = emptyList(),
val selectedTeamSeason: String? = null,
val notificationTime: String = "09:00",
@@ -282,6 +283,7 @@ data class PushTokenRequest(
val token: String,
val platform: String = "android",
val appVersion: String? = null,
val installationId: String? = null,
)
data class BirthdayDto(
val name: String = "",
@@ -302,6 +304,7 @@ data class QttrRowDto(
val playerName: String = "",
val clubName: String = "",
val currentQttr: Int? = null,
val currentTtr: Int? = null,
val previousQttr: Int? = null,
val birthdate: String? = null,
)

View File

@@ -21,7 +21,7 @@ class HarheimerMessagingService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
super.onNewToken(token)
serviceScope.launch {
pushTokenRepository.registerToken(token)
pushTokenRepository.registerCurrentDevice()
}
}

View File

@@ -81,6 +81,7 @@ object HarheimerNotifications {
"news", "news_expiring" -> Destinations.MemberNews.route
"event", "events_today", "events_tomorrow" -> Destinations.Termine.route
"team_matches" -> Destinations.Spielplan.route
"qttr_list_updated", "ttr_change" -> Destinations.Qttr.route
"birthdays" -> Destinations.MemberArea.route
"contact_request" -> Destinations.CmsContactRequests.route
"user_registration" -> Destinations.CmsBenutzer.route

View File

@@ -19,6 +19,7 @@ data class NotificationPreferences(
val birthdays: Boolean = false,
val newContactRequest: Boolean = false,
val newUserRegistration: Boolean = false,
val ownTtrChanges: Boolean = false,
val selectedTeamSlugs: Set<String> = emptySet(),
val selectedTeamSeason: String? = null,
val notificationTime: String = DEFAULT_NOTIFICATION_TIME,
@@ -43,6 +44,7 @@ class NotificationPreferencesRepository @Inject constructor(
birthdays = preferences.getBoolean(KEY_BIRTHDAYS, false),
newContactRequest = preferences.getBoolean(KEY_NEW_CONTACT_REQUEST, false),
newUserRegistration = preferences.getBoolean(KEY_NEW_USER_REGISTRATION, false),
ownTtrChanges = preferences.getBoolean(KEY_OWN_TTR_CHANGES, false),
selectedTeamSlugs = preferences.getStringSet(KEY_SELECTED_TEAM_SLUGS, emptySet()).orEmpty(),
selectedTeamSeason = preferences.getString(KEY_SELECTED_TEAM_SEASON, null)?.takeIf { it.isNotBlank() },
notificationTime = preferences.getString(KEY_NOTIFICATION_TIME, DEFAULT_NOTIFICATION_TIME) ?: DEFAULT_NOTIFICATION_TIME,
@@ -69,6 +71,7 @@ class NotificationPreferencesRepository @Inject constructor(
.putBoolean(KEY_BIRTHDAYS, settings.birthdays)
.putBoolean(KEY_NEW_CONTACT_REQUEST, settings.newContactRequest)
.putBoolean(KEY_NEW_USER_REGISTRATION, settings.newUserRegistration)
.putBoolean(KEY_OWN_TTR_CHANGES, settings.ownTtrChanges)
.putStringSet(KEY_SELECTED_TEAM_SLUGS, settings.selectedTeamSlugs)
.putString(KEY_SELECTED_TEAM_SEASON, settings.selectedTeamSeason)
.putString(KEY_NOTIFICATION_TIME, settings.notificationTime)
@@ -98,6 +101,7 @@ class NotificationPreferencesRepository @Inject constructor(
const val KEY_BIRTHDAYS = "birthdays"
const val KEY_NEW_CONTACT_REQUEST = "new_contact_request"
const val KEY_NEW_USER_REGISTRATION = "new_user_registration"
const val KEY_OWN_TTR_CHANGES = "own_ttr_changes"
const val KEY_SELECTED_TEAM_SLUGS = "selected_team_slugs"
const val KEY_SELECTED_TEAM_SEASON = "selected_team_season"
const val KEY_NOTIFICATION_TIME = "notification_time"
@@ -114,6 +118,7 @@ private fun NotificationSettingsDto.toPreferences(): NotificationPreferences = N
birthdays = birthdays,
newContactRequest = newContactRequest,
newUserRegistration = newUserRegistration,
ownTtrChanges = ownTtrChanges,
selectedTeamSlugs = selectedTeamSlugs.toSet(),
selectedTeamSeason = selectedTeamSeason,
notificationTime = notificationTime,
@@ -129,6 +134,7 @@ private fun NotificationPreferences.toDto(): NotificationSettingsDto = Notificat
birthdays = birthdays,
newContactRequest = newContactRequest,
newUserRegistration = newUserRegistration,
ownTtrChanges = ownTtrChanges,
selectedTeamSlugs = selectedTeamSlugs.toList(),
selectedTeamSeason = selectedTeamSeason,
notificationTime = notificationTime,

View File

@@ -1,6 +1,7 @@
package de.harheimertc.repositories
import android.util.Log
import com.google.firebase.installations.FirebaseInstallations
import com.google.firebase.messaging.FirebaseMessaging
import de.harheimertc.BuildConfig
import de.harheimertc.data.ApiService
@@ -15,19 +16,24 @@ class PushTokenRepository @Inject constructor(
) {
suspend fun registerCurrentDevice(): Result<Unit> = runCatching {
val token = FirebaseMessaging.getInstance().token.await()
registerToken(token).getOrThrow()
val installationId = FirebaseInstallations.getInstance().id.await()
registerToken(token, installationId).getOrThrow()
}
suspend fun registerToken(token: String): Result<Unit> = runCatching {
suspend fun registerToken(token: String, installationId: String? = null): Result<Unit> = runCatching {
if (token.isBlank()) return@runCatching
retryOnNetworkFailure {
val response = api.registerPushToken(
PushTokenRequest(
token = token,
appVersion = "${BuildConfig.VERSION_NAME}+${BuildConfig.VERSION_CODE}",
installationId = installationId,
),
)
if (!response.isSuccessful) error("Push-Token konnte nicht registriert werden.")
if (!response.isSuccessful) {
val detail = response.errorBody()?.string().orEmpty().replace(Regex("\\s+"), " ").take(180)
error("Push-Token konnte nicht registriert werden (HTTP ${response.code()})${if (detail.isBlank()) "" else ": $detail"}")
}
}
}.onFailure { error ->
Log.w("PushTokenRepository", "Push-Token Registrierung fehlgeschlagen", error)

View File

@@ -472,7 +472,7 @@ private fun submenu(section: MenuSection?, state: NavigationUiState): List<MenuT
MenuSection.INTERN -> buildList {
add(MenuTarget("Übersicht", Destinations.MemberArea.route))
add(MenuTarget("Mitgliederliste", Destinations.Members.route))
add(MenuTarget("QTTR", Destinations.Qttr.route))
add(MenuTarget("TTR / QTTR", Destinations.Qttr.route))
add(MenuTarget("News", Destinations.MemberNews.route))
add(MenuTarget("Mein Profil", Destinations.Profile.route))
add(MenuTarget("Benachrichtigungen", Destinations.NotificationSettings.route))

View File

@@ -6,6 +6,7 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import de.harheimertc.repositories.LoginRepository
import de.harheimertc.repositories.PasskeyRepository
import de.harheimertc.repositories.PushTokenRepository
import de.harheimertc.ui.components.isValidEmail
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -29,6 +30,7 @@ data class LoginUiState(
class LoginViewModel @Inject constructor(
private val repository: LoginRepository,
private val passkeyRepository: PasskeyRepository,
private val pushTokenRepository: PushTokenRepository,
) : ViewModel() {
private val _state = MutableStateFlow(LoginUiState())
val state: StateFlow<LoginUiState> = _state
@@ -70,6 +72,7 @@ class LoginViewModel @Inject constructor(
_state.value = current.copy(loading = true, error = null, message = null)
repository.login(current.email, current.password)
.onSuccess { response ->
viewModelScope.launch { pushTokenRepository.registerCurrentDevice() }
_state.value = current.copy(
password = "",
loading = false,
@@ -92,6 +95,7 @@ class LoginViewModel @Inject constructor(
_state.value = current.copy(loading = true, error = null, message = null)
passkeyRepository.login(context, current.email)
.onSuccess { response ->
viewModelScope.launch { pushTokenRepository.registerCurrentDevice() }
_state.value = current.copy(
password = "",
loading = false,

View File

@@ -28,7 +28,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalUriHandler
import android.util.Log
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
@@ -248,18 +247,7 @@ fun QttrScreen(
viewModel: QttrViewModel = hiltViewModel(),
) {
val state by viewModel.state.collectAsState()
val uriHandler = LocalUriHandler.current
val externalUrl = "https://www.mytischtennis.de/rankings/andro-rangliste?continent=all&country=Deutschland&all-players=on&as=DE.WE.R4.07&di=DE.WE.R4.07.04&area=DE.WE.R4.07.04.43&clubnr-search=Harheimer+TC&clubnr=43030&fednickname=HeTTV&gender=all&current-ranking=yes&ttr-range=100%3B3000&birth-range=1926%3B2021"
MemberAreaPage(navController, showBackNavigation, "QTTR-Werte", "Aus technischen Gründen sind nur die QTTR-Werte verfügbar.") {
item {
Surface(color = Primary100, shape = RoundedCornerShape(12.dp)) {
Column(Modifier.fillMaxWidth().padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text("Für TTR bitte die myTischtennis-Rangliste verwenden.", color = Primary900)
TextButton(onClick = { uriHandler.openUri(externalUrl) }) { Text("myTischtennis öffnen") }
}
}
}
MemberAreaPage(navController, showBackNavigation, "TTR- und QTTR-Werte", "Aktuelle Werte der Vereinsmitglieder aus myTischtennis.") {
item {
Surface(color = Color.White, shape = RoundedCornerShape(14.dp), shadowElevation = 3.dp) {
Column(Modifier.fillMaxWidth().padding(18.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
@@ -269,9 +257,9 @@ fun QttrScreen(
}
}
when {
state.loading -> item { LoadingState("QTTR-Werte werden geladen...") }
state.loading -> item { LoadingState("TTR- und QTTR-Werte werden geladen...") }
state.error != null -> item { ErrorCard(state.error.orEmpty(), viewModel::load) }
state.rows.isEmpty() -> item { Text("Keine QTTR-Werte gefunden.", color = Accent700) }
state.rows.isEmpty() -> item { Text("Keine TTR- oder QTTR-Werte gefunden.", color = Accent700) }
else -> items(state.rows.size) { index -> QttrRowCard(state.rows[index], isOwnRow(state.rows[index].playerName, state.currentUserName)) }
}
}
@@ -388,7 +376,10 @@ private fun QttrRowCard(row: QttrRowDto, highlighted: Boolean) {
)
Text(row.clubName.ifBlank { "Harheimer TC" }, color = qttrNameColor(row.gender, isMinor(row.birthdate)).copy(alpha = 0.88f))
}
Text(row.currentQttr?.toString() ?: "-", color = Primary600, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
Column(horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.spacedBy(3.dp)) {
Text("TTR ${row.currentTtr?.toString() ?: "-"}", color = Accent700, style = MaterialTheme.typography.labelLarge)
Text("QTTR ${row.currentQttr?.toString() ?: "-"}", color = Primary600, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
}
}
}
}

View File

@@ -121,9 +121,9 @@ private fun MemberAreaCardGrid(navController: NavController) {
onClick = { navController.navigate(Destinations.MemberNews.route) },
)
MemberAreaCard(
title = "QTTR",
description = "Aktuelle QTTR-Werte der Vereinsmitglieder",
marker = "Q",
title = "TTR / QTTR",
description = "Aktuelle TTR- und QTTR-Werte der Vereinsmitglieder",
marker = "T",
onClick = { navController.navigate(Destinations.Qttr.route) },
)
}

View File

@@ -40,6 +40,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import de.harheimertc.notifications.HarheimerNotifications
import de.harheimertc.BuildConfig
import de.harheimertc.repositories.Mannschaft
import de.harheimertc.repositories.NotificationPreferences
import de.harheimertc.ui.components.LoadingState
@@ -103,6 +104,23 @@ fun NotificationSettingsScreen(
}
}
item {
NotificationCard("Geräte-Registrierung") {
Text("Der Push-Token wird dem aktuell angemeldeten Konto auf diesem Server zugeordnet:", color = Accent700)
Text(BuildConfig.API_BASE_URL, color = Accent900, style = MaterialTheme.typography.bodySmall)
Button(
onClick = viewModel::registerCurrentDevice,
enabled = !state.deviceRegistrationInProgress,
modifier = Modifier.fillMaxWidth(),
) {
Text(if (state.deviceRegistrationInProgress) "Gerät wird registriert..." else "Android-Gerät jetzt registrieren")
}
state.deviceRegistrationMessage?.let { message ->
Text(message, color = if (state.deviceRegistrationError) MaterialTheme.colorScheme.error else Color(0xFF166534))
}
}
}
if (state.loading) {
item { LoadingState("Benachrichtigungseinstellungen werden geladen...") }
} else {
@@ -162,6 +180,9 @@ fun NotificationSettingsScreen(
ToggleRow("Geburtstage", state.settings.birthdays) {
viewModel.update(state.settings.copy(birthdays = it))
}
ToggleRow("Änderung meines TTR-Werts", state.settings.ownTtrChanges) {
viewModel.update(state.settings.copy(ownTtrChanges = it))
}
}
}

View File

@@ -8,6 +8,7 @@ import de.harheimertc.repositories.LoginRepository
import de.harheimertc.repositories.MannschaftenRepository
import de.harheimertc.repositories.NotificationPreferences
import de.harheimertc.repositories.NotificationPreferencesRepository