168 lines
6.3 KiB
Python
168 lines
6.3 KiB
Python
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.core.security import utc_now
|
|
from app.db.base import get_db
|
|
from app.models import Notification, NotificationPreference
|
|
from app.schemas import NotificationPreferencePatch
|
|
from app.services.auth import CurrentContext, get_current_context
|
|
from app.services.dashboard import calendar_items, file_items, home_dashboard, local_actions_for_context
|
|
from app.services.serializers import profile_dict
|
|
|
|
router = APIRouter(prefix="/api", tags=["home"])
|
|
|
|
|
|
DEFAULT_PREFERENCES = {
|
|
"direct_mentions": "immediate",
|
|
"event_changes": "immediate",
|
|
"urgent_announcements": "immediate",
|
|
"tasks_assigned": "immediate",
|
|
"discussions": "digest",
|
|
"new_files": "digest",
|
|
"general_chatter": "digest",
|
|
"reactions": "muted",
|
|
"off_topic": "muted",
|
|
}
|
|
|
|
|
|
@router.get("/home")
|
|
def home(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
|
|
dashboard = home_dashboard(db, ctx)
|
|
return {
|
|
"profile": profile_dict(ctx.home_profile, ctx.member),
|
|
"sections": dashboard["sections"],
|
|
"connections": dashboard["connections"],
|
|
}
|
|
|
|
|
|
@router.get("/home/actions")
|
|
def home_actions(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
|
|
return {"actions": home_dashboard(db, ctx)["sections"]["needs_me"]}
|
|
|
|
|
|
@router.get("/home/calendar")
|
|
def home_calendar(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
|
|
return {"events": calendar_items(db, ctx)}
|
|
|
|
|
|
@router.get("/home/files")
|
|
def home_files(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
|
|
return {"files": file_items(db, ctx)}
|
|
|
|
|
|
@router.get("/home/official-updates")
|
|
def official_updates(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
|
|
return {"official_updates": home_dashboard(db, ctx)["sections"]["official_updates"]}
|
|
|
|
|
|
@router.get("/home/catch-up")
|
|
def catch_up(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
|
|
return {"catch_up": home_dashboard(db, ctx)["sections"]["catch_up"]}
|
|
|
|
|
|
def _preference_owner(ctx: CurrentContext) -> tuple[str | None, str | None]:
|
|
if ctx.home_profile:
|
|
return ctx.home_profile.id, None
|
|
if ctx.member:
|
|
return None, ctx.member.id
|
|
return None, None
|
|
|
|
|
|
@router.get("/me/notification-preferences")
|
|
def get_notification_preferences(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
|
|
home_profile_id, member_id = _preference_owner(ctx)
|
|
rows = db.scalars(
|
|
select(NotificationPreference).where(
|
|
NotificationPreference.home_profile_id == home_profile_id,
|
|
NotificationPreference.member_id == member_id,
|
|
)
|
|
).all()
|
|
result = dict(DEFAULT_PREFERENCES)
|
|
for row in rows:
|
|
result[row.category] = row.delivery
|
|
return {
|
|
"headline": "Mute the noise, not the group.",
|
|
"preferences": result,
|
|
"groups": {
|
|
"Immediate": ["direct_mentions", "event_changes", "urgent_announcements", "tasks_assigned"],
|
|
"Quiet / digest": ["discussions", "new_files", "general_chatter"],
|
|
"Mute": ["reactions", "off_topic"],
|
|
},
|
|
}
|
|
|
|
|
|
@router.patch("/me/notification-preferences")
|
|
def patch_notification_preferences(
|
|
payload: NotificationPreferencePatch,
|
|
ctx: CurrentContext = Depends(get_current_context),
|
|
db: Session = Depends(get_db),
|
|
) -> dict:
|
|
home_profile_id, member_id = _preference_owner(ctx)
|
|
if home_profile_id is None and member_id is None:
|
|
raise HTTPException(status_code=401, detail={"error": {"code": "not_authenticated", "message": "Open an invite first.", "details": {}}})
|
|
for category, delivery in payload.preferences.items():
|
|
row = db.scalar(
|
|
select(NotificationPreference).where(
|
|
NotificationPreference.home_profile_id == home_profile_id,
|
|
NotificationPreference.member_id == member_id,
|
|
NotificationPreference.category == category,
|
|
)
|
|
)
|
|
if row:
|
|
row.delivery = delivery
|
|
row.enabled = delivery != "muted"
|
|
row.updated_at = utc_now()
|
|
else:
|
|
db.add(
|
|
NotificationPreference(
|
|
home_profile_id=home_profile_id,
|
|
member_id=member_id,
|
|
category=category,
|
|
delivery=delivery,
|
|
enabled=delivery != "muted",
|
|
)
|
|
)
|
|
db.commit()
|
|
return get_notification_preferences(ctx, db)
|
|
|
|
|
|
@router.get("/me/notifications")
|
|
def notifications(ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
|
|
query = select(Notification)
|
|
if ctx.home_profile:
|
|
query = query.where(Notification.home_profile_id == ctx.home_profile.id)
|
|
elif ctx.member:
|
|
query = query.where(Notification.member_id == ctx.member.id)
|
|
rows = db.scalars(query.order_by(Notification.created_at.desc()).limit(50)).all()
|
|
return {
|
|
"notifications": [
|
|
{
|
|
"id": item.id,
|
|
"title": item.title,
|
|
"body": item.body,
|
|
"category": item.category,
|
|
"read_at": item.read_at.isoformat() if item.read_at else None,
|
|
"created_at": item.created_at.isoformat(),
|
|
}
|
|
for item in rows
|
|
]
|
|
}
|
|
|
|
|
|
@router.patch("/me/notifications/{notification_id}/read")
|
|
def mark_notification_read(notification_id: str, ctx: CurrentContext = Depends(get_current_context), db: Session = Depends(get_db)) -> dict:
|
|
query = select(Notification).where(Notification.id == notification_id)
|
|
if ctx.home_profile:
|
|
query = query.where(Notification.home_profile_id == ctx.home_profile.id)
|
|
elif ctx.member:
|
|
query = query.where(Notification.member_id == ctx.member.id)
|
|
notification = db.scalar(query)
|
|
if not notification:
|
|
raise HTTPException(status_code=404, detail={"error": {"code": "not_found", "message": "Notification not found.", "details": {}}})
|
|
notification.read_at = utc_now()
|
|
db.commit()
|
|
return {"ok": True}
|