diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/notifications.py')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/api/notifications.py | 61 |
1 files changed, 49 insertions, 12 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/notifications.py b/packages/meshbay-hub/src/meshbay_hub/api/notifications.py index ca56620..9d5c125 100644 --- a/packages/meshbay-hub/src/meshbay_hub/api/notifications.py +++ b/packages/meshbay-hub/src/meshbay_hub/api/notifications.py @@ -1,9 +1,28 @@ -"""Notification endpoints — /v1/notifications/*""" +""" +Notification endpoints — /v1/notifications/* + +**Dismissing one deletes it.** These are signals, not a record: the group is +still there, the message is still in the chat, the invitation is still an +invitation, so nothing is lost by dropping the row — which is what +`purge_notifications` below has always said, now applied to one at a time. + +That resolves a disagreement between two halves that were each defensible +alone. The interface treats a click as "this is gone" and removes the entry; +the hub marked it read and kept it; and the next launch listed read entries +too, so everything dismissed came back. Filtering the list to unread fixed +what the user saw and left the rows accumulating for nothing, invisible for +ever — which is the state this replaces. + +`Notification.read` is therefore **vestigial**: nothing stored can be read, +because reading it deletes it. It stays because dropping a column is a +migration for no gain, and `unread_only` stays because it is what an older +interface asks for and it still answers correctly — every row is unread. +""" from fastapi import APIRouter, Depends, HTTPException from datetime import datetime, timezone -from sqlalchemy import delete, func, select, update +from sqlalchemy import delete, func, select from sqlalchemy.ext.asyncio import AsyncSession from meshbay_hub.api.deps import get_current_user @@ -52,16 +71,27 @@ async def list_notifications( } +@router.delete("/{notification_id}") @router.post("/{notification_id}/read") -async def mark_read( +async def dismiss( notification_id: int, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): + """ + Dismiss one. The row goes. + + Two paths to the same handler. `DELETE /{id}` says what happens and is what + the interface calls; `POST /{id}/read` is what every already-installed + client calls, and it has to keep working — the SPA ships inside the desktop + package, so a hub is always talking to some interface older than itself. + Giving the old path the new behaviour means those clients stop accumulating + rows too, rather than only the ones that have been updated. + """ notif = await db.get(Notification, notification_id) if not notif or notif.user_id != current_user.id: raise HTTPException(status_code=404, detail="Notification not found") - notif.read = True + await db.delete(notif) await db.commit() return {"status": "ok"} @@ -76,7 +106,8 @@ async def purge_notifications( These are signals, not a record: the group is still there, the message is still in the chat, the invitation is still an invitation. Nothing is lost by - clearing the list, so it clears rather than marking a hundred rows read. + clearing the list, so it clears rather than marking a hundred rows read — + the reasoning the whole module now follows. """ result = await db.execute( delete(Notification).where(Notification.user_id == current_user.id)) @@ -85,17 +116,23 @@ async def purge_notifications( @router.post("/read-all") -async def mark_all_read( +async def dismiss_all( current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): - await db.execute( - update(Notification) - .where(Notification.user_id == current_user.id, Notification.read == False) # noqa: E712 - .values(read=True) - ) + """ + Dismiss every one — the same thing as `DELETE ""`, under the name an older + client knows it by. + + Marking them read instead would put back exactly what this change removes: + rows the list can never show again. Nothing in this repository calls it, + but an endpoint that is reachable is an endpoint that can be called, and it + should not be the one route that still hoards. + """ + result = await db.execute( + delete(Notification).where(Notification.user_id == current_user.id)) await db.commit() - return {"status": "ok"} + return {"status": "ok", "removed": result.rowcount} async def create_notification( |