diff --git a/backend/services/main/push_service.py b/backend/services/main/push_service.py index abd2934..ae69cff 100644 --- a/backend/services/main/push_service.py +++ b/backend/services/main/push_service.py @@ -205,6 +205,7 @@ class PushNotificationService: title, body, payload_data, + include_notification=False, ) logger.info( "FCM dm push sent recipient=%s token=%s response=%s", @@ -275,25 +276,44 @@ class PushNotificationService: except Exception as e: logger.error(f"Failed to send push notification to user {user_id}: {e}") - def _send_fcm_to_token(self, token: str, title: str, body: str, data: dict): - """Send an FCM data-only push to a single device token using Firebase Admin SDK. - Notification display is handled by the app, not FCM.""" + def _send_fcm_to_token(self, token: str, title: str, body: str, data: dict, include_notification: bool = True): + """Send an FCM push to a single device token using Firebase Admin SDK. + + By default this sends both notification + data payloads, but callers can disable + the notification payload for custom client-side rendering. + """ if not self.firebase_initialized: raise RuntimeError("Firebase Admin SDK not initialized") try: - # Send only data payload - let the app handle notification display - # This prevents FCM from auto-showing notifications - msg = firebase_messaging.Message( - token=token, - data={ - "title": title, - "body": body, - **{k: str(v) for k, v in (data or {}).items()} - }, - android=firebase_messaging.AndroidConfig(priority="high"), - apns=firebase_messaging.APNSConfig(headers={"apns-priority": "10"}) - ) + payload = { + "title": title, + "body": body, + **{k: str(v) for k, v in (data or {}).items()} + } + + if include_notification: + # Send notification + data payload: + # notification ensures visibility in system tray when app is background, + # data keeps app-level handling usable when app is foreground. + msg = firebase_messaging.Message( + token=token, + notification=firebase_messaging.Notification( + title=title, + body=body, + ), + data=payload, + android=firebase_messaging.AndroidConfig(priority="high"), + apns=firebase_messaging.APNSConfig(headers={"apns-priority": "10"}) + ) + else: + # Data-only push for custom client-side rendering. + msg = firebase_messaging.Message( + token=token, + data=payload, + android=firebase_messaging.AndroidConfig(priority="high"), + apns=firebase_messaging.APNSConfig(headers={"apns-priority": "10"}) + ) resp = firebase_messaging.send(msg) logger.debug("Firebase message queued token=%s", self._short_token(token)) return resp diff --git a/backend/services/main/routes/envelope_messaging.py b/backend/services/main/routes/envelope_messaging.py index 3efa2aa..0f2d75e 100644 --- a/backend/services/main/routes/envelope_messaging.py +++ b/backend/services/main/routes/envelope_messaging.py @@ -38,6 +38,7 @@ from ..service_calls import ( delete_resumable_upload_in_storage, ) from .messaging import messagingManager, convert_dm_envelope +from ..push_service import push_service logger = logging.getLogger("uvicorn.error") @@ -367,6 +368,11 @@ async def send_encrypted_message( sender_payload["client_message_id"] = request.client_message_id await messagingManager.send_update_to_user(dm_envelope.sender_id, "dmNew", sender_payload, db) + try: + await push_service.send_dm_notification(db, dm_envelope, current_user) + except Exception as e: + logger.error("Failed to send push notification for DM %s: %s", dm_envelope.id, e) + return { "id": dm_envelope.id, "sender_id": dm_envelope.sender_id, diff --git a/backend/services/main/routes/messaging.py b/backend/services/main/routes/messaging.py index 5335345..eb347c2 100644 --- a/backend/services/main/routes/messaging.py +++ b/backend/services/main/routes/messaging.py @@ -284,6 +284,7 @@ def convert_dm_envelope(db: Session, envelope: DMEnvelope, user_id: int | None = "id": envelope.id, "senderId": envelope.sender_id, "recipientId": envelope.recipient_id, + "sender_username": sender.username if sender else f"user_{envelope.sender_id}", "iv_b64": envelope.iv_b64, "ciphertext_b64": envelope.ciphertext_b64, "wrapped_mek_b64": wrapped_mek_b64,