From 14cd5f04d5a1828acfcf25d38759db329642e4fa Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Wed, 10 Jun 2026 16:36:50 +0200 Subject: [PATCH] fixed deploy and navigation --- android-app/app/src/main/AndroidManifest.xml | 3 +- .../main/java/de/harheimertc/MainActivity.kt | 39 ++++++++++++++++++- .../HarheimerMessagingService.kt | 2 +- .../notifications/HarheimerNotifications.kt | 27 +++++++++++++ deploy-production.sh | 20 +++++++++- deploy-test.sh | 14 ++++++- 6 files changed, 97 insertions(+), 8 deletions(-) diff --git a/android-app/app/src/main/AndroidManifest.xml b/android-app/app/src/main/AndroidManifest.xml index 5fc3c8c..39c7885 100644 --- a/android-app/app/src/main/AndroidManifest.xml +++ b/android-app/app/src/main/AndroidManifest.xml @@ -11,7 +11,8 @@ android:theme="@style/Theme.HarheimerTC" android:usesCleartextTraffic="${usesCleartextTraffic}"> + android:exported="true" + android:launchMode="singleTop"> diff --git a/android-app/app/src/main/java/de/harheimertc/MainActivity.kt b/android-app/app/src/main/java/de/harheimertc/MainActivity.kt index 2921440..03f6766 100644 --- a/android-app/app/src/main/java/de/harheimertc/MainActivity.kt +++ b/android-app/app/src/main/java/de/harheimertc/MainActivity.kt @@ -1,12 +1,15 @@ package de.harheimertc import android.Manifest +import android.content.Intent import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.compose.setContent import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.tooling.preview.Preview import androidx.navigation.compose.rememberNavController import de.harheimertc.ui.navigation.NavGraph @@ -21,6 +24,8 @@ import androidx.compose.ui.platform.LocalContext @AndroidEntryPoint class MainActivity : ComponentActivity() { + private val notificationRoute = mutableStateOf(null) + private val notificationPermissionLauncher = registerForActivityResult( ActivityResultContracts.RequestPermission(), ) { granted -> @@ -30,26 +35,56 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) requestNotificationPermissionIfNeeded() + notificationRoute.value = extractNotificationRoute(intent) setContent { - App() + App( + notificationRoute = notificationRoute.value, + onNotificationRouteConsumed = { notificationRoute.value = null }, + ) } } + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + setIntent(intent) + notificationRoute.value = extractNotificationRoute(intent) + } + + private fun extractNotificationRoute(intent: Intent?): String? = + intent?.getStringExtra(EXTRA_NOTIFICATION_ROUTE)?.takeIf { it.isNotBlank() } + private fun requestNotificationPermissionIfNeeded() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && !HarheimerNotifications.hasNotificationPermission(this)) { notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) } } + + companion object { + const val EXTRA_NOTIFICATION_TYPE = "de.harheimertc.extra.NOTIFICATION_TYPE" + const val EXTRA_NOTIFICATION_ROUTE = "de.harheimertc.extra.NOTIFICATION_ROUTE" + const val EXTRA_NEWS_ID = "de.harheimertc.extra.NEWS_ID" + } } @Composable -fun App() { +fun App( + notificationRoute: String? = null, + onNotificationRouteConsumed: () -> Unit = {}, +) { HarheimerTheme { val navController = rememberNavController() val ctx = LocalContext.current val activity = ctx as? ComponentActivity Log.i("HILT_FACTORY", "defaultViewModelProviderFactory=${activity?.defaultViewModelProviderFactory?.javaClass?.name}") val navigationViewModel: NavigationViewModel = hiltViewModel() + LaunchedEffect(notificationRoute) { + val route = notificationRoute?.takeIf { it.isNotBlank() } ?: return@LaunchedEffect + navController.navigate(route) { + launchSingleTop = true + popUpTo(Destinations.Home.route) + } + onNotificationRouteConsumed() + } NavGraph(navController = navController, navigationViewModelParam = navigationViewModel) } } diff --git a/android-app/app/src/main/java/de/harheimertc/notifications/HarheimerMessagingService.kt b/android-app/app/src/main/java/de/harheimertc/notifications/HarheimerMessagingService.kt index 0e3cf50..a4efcde 100644 --- a/android-app/app/src/main/java/de/harheimertc/notifications/HarheimerMessagingService.kt +++ b/android-app/app/src/main/java/de/harheimertc/notifications/HarheimerMessagingService.kt @@ -37,7 +37,7 @@ class HarheimerMessagingService : FirebaseMessagingService() { val notificationId = message.data["notificationId"]?.toIntOrNull() ?: message.messageId?.hashCode() ?: System.currentTimeMillis().toInt() - val shown = HarheimerNotifications.showBasicNotification(this, notificationId, title, body) + val shown = HarheimerNotifications.showBasicNotification(this, notificationId, title, body, message.data) Log.d("HarheimerMessaging", "Push message received type=${message.data["type"]}, shown=$shown") } } diff --git a/android-app/app/src/main/java/de/harheimertc/notifications/HarheimerNotifications.kt b/android-app/app/src/main/java/de/harheimertc/notifications/HarheimerNotifications.kt index f8aec64..848a4f9 100644 --- a/android-app/app/src/main/java/de/harheimertc/notifications/HarheimerNotifications.kt +++ b/android-app/app/src/main/java/de/harheimertc/notifications/HarheimerNotifications.kt @@ -3,13 +3,17 @@ package de.harheimertc.notifications import android.Manifest import android.app.NotificationChannel import android.app.NotificationManager +import android.app.PendingIntent import android.content.Context +import android.content.Intent import android.content.pm.PackageManager import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat +import de.harheimertc.MainActivity import de.harheimertc.R +import de.harheimertc.ui.navigation.Destinations object HarheimerNotifications { const val DEFAULT_CHANNEL_ID = "harheimer_tc_updates" @@ -35,6 +39,7 @@ object HarheimerNotifications { notificationId: Int, title: String, message: String, + data: Map = emptyMap(), ): Boolean { if (!hasNotificationPermission(context)) return false val notification = NotificationCompat.Builder(context, DEFAULT_CHANNEL_ID) @@ -43,9 +48,31 @@ object HarheimerNotifications { .setContentText(message) .setStyle(NotificationCompat.BigTextStyle().bigText(message)) .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setContentIntent(createContentIntent(context, notificationId, data)) .setAutoCancel(true) .build() NotificationManagerCompat.from(context).notify(notificationId, notification) return true } + + private fun createContentIntent(context: Context, notificationId: Int, payload: Map): PendingIntent { + val route = destinationRoute(payload) + val intent = Intent(context, MainActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP + putExtra(MainActivity.EXTRA_NOTIFICATION_TYPE, payload["type"]) + putExtra(MainActivity.EXTRA_NOTIFICATION_ROUTE, route) + payload["newsId"]?.let { putExtra(MainActivity.EXTRA_NEWS_ID, it) } + } + return PendingIntent.getActivity( + context, + notificationId, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, + ) + } + + private fun destinationRoute(data: Map): String = when (data["type"]) { + "news" -> Destinations.MemberNews.route + else -> Destinations.Home.route + } } diff --git a/deploy-production.sh b/deploy-production.sh index ea5e665..1788df6 100755 --- a/deploy-production.sh +++ b/deploy-production.sh @@ -279,9 +279,25 @@ use_project_node ensure_node_version install_dependencies_if_needed -# 4. Remove old build (but keep data!) +# 4. Stop running apps before replacing build artifacts echo "" -echo "4. Cleaning build artifacts..." +echo "4. Stopping PM2 before replacing build artifacts..." +if command -v pm2 >/dev/null 2>&1; then + for instance_name in harheimertc harheimertc-3102; do + if pm2 describe "$instance_name" >/dev/null 2>&1; then + pm2 stop "$instance_name" || true + echo " ✓ $instance_name gestoppt" + else + echo " PM2-Prozess $instance_name läuft nicht" + fi + done +else + echo " PM2 ist nicht verfügbar" +fi + +# 5. Remove old build (but keep data!) +echo "" +echo "5. Cleaning build artifacts..." # Sicherstellen, dass .output vollständig gelöscht wird if [ -d ".output" ]; then echo " Removing .output directory..." diff --git a/deploy-test.sh b/deploy-test.sh index 2751283..237afdf 100755 --- a/deploy-test.sh +++ b/deploy-test.sh @@ -285,9 +285,19 @@ use_project_node ensure_node_version install_dependencies_if_needed -# 4. Remove old build (but keep data!) +# 4. Stop running app before replacing build artifacts echo "" -echo "4. Cleaning build artifacts..." +echo "4. Stopping PM2 before replacing build artifacts..." +if command -v pm2 >/dev/null 2>&1 && pm2 describe harheimertc.test >/dev/null 2>&1; then + pm2 stop harheimertc.test || true + echo " ✓ harheimertc.test gestoppt" +else + echo " PM2-Prozess harheimertc.test läuft nicht oder PM2 ist nicht verfügbar" +fi + +# 5. Remove old build (but keep data!) +echo "" +echo "5. Cleaning build artifacts..." # Sicherstellen, dass .output vollständig gelöscht wird if [ -d ".output" ]; then echo " Removing .output directory..."