"""In-app notifications for the mobile client.""" from datetime import datetime, timezone from bson import ObjectId from pymongo import DESCENDING from ..core.db_connector import notifications_collection def _ensure_indexes(): try: notifications_collection.create_index( [("user_id", 1), ("created_at", DESCENDING)] ) notifications_collection.create_index([("user_id", 1), ("is_read", 1)]) except Exception: pass _ensure_indexes() def _serialize(doc, user_id=None): if not doc: return None read_by = [str(u) for u in doc.get("read_by", [])] uid = str(user_id) if user_id is not None else "" created = doc.get("created_at") return { "id": str(doc["_id"]), "title": doc.get("title") or "", "message": doc.get("message") or "", "type": doc.get("type") or "info", "created_at": created.isoformat() if created else datetime.now(timezone.utc).isoformat(), "is_read": uid in read_by if uid else bool(doc.get("is_read")), } def list_notifications(user_id, limit=50): uid = str(user_id) cursor = notifications_collection.find( {"$or": [{"user_id": uid}, {"user_id": {"$exists": False}}, {"broadcast": True}]} ).sort("created_at", DESCENDING).limit(limit) return [_serialize(doc, uid) for doc in cursor] def mark_read(user_id, notification_id): uid = str(user_id) try: oid = ObjectId(notification_id) except Exception: return False result = notifications_collection.update_one( {"_id": oid}, {"$addToSet": {"read_by": uid}, "$set": {"is_read": True}}, ) return result.matched_count > 0 def mark_all_read(user_id): uid = str(user_id) notifications_collection.update_many( {"$or": [{"user_id": uid}, {"broadcast": True}]}, {"$addToSet": {"read_by": uid}}, ) return True def delete_notification(user_id, notification_id): uid = str(user_id) try: oid = ObjectId(notification_id) except Exception: return False result = notifications_collection.delete_one( {"_id": oid, "$or": [{"user_id": uid}, {"broadcast": True}]} ) return result.deleted_count > 0