aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/api/notifications.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-09 12:08:31 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-09 12:08:31 +0200
commit6832df6177ad973ad0e1b4f0a49d7a6da06c6e04 (patch)
treed9040ce0da5d82400d1b973344615ca1c6b67b3c /packages/meshbay-hub/src/meshbay_hub/api/notifications.py
parent2860f1de75af1d44d35292ecbf79c68f02409d19 (diff)
downloadmeshbay-6832df6177ad973ad0e1b4f0a49d7a6da06c6e04.tar.gz
feat: notifications on Android while closed, with nothing to install
The phone fetches what is new every fifteen minutes with a poll secret (POST /v1/push/poll) that reads notification lines and nothing else. When a UnifiedPush distributor is already installed, the hub also pushes at once, encrypted to the phone (RFC 8291); losing the distributor falls back to fetching. The hub now honours "disable all notifications" itself: create_notification creates nothing for that account, as it already did for a muted group, so neither switch lets anything reach a phone. The interface used to be the only reader of the account-wide switch. Push endpoints are member-supplied URLs: a send refuses non-public addresses, connects to the address it checked, and follows no redirect. Android build untested here (no SDK on this machine). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/notifications.py')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/notifications.py23
1 files changed, 20 insertions, 3 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/notifications.py b/packages/meshbay-hub/src/meshbay_hub/api/notifications.py
index e4bac2e..128c0d1 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/notifications.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/notifications.py
@@ -26,8 +26,9 @@ from sqlalchemy import delete, func, select
from sqlalchemy.ext.asyncio import AsyncSession
from meshbay_hub.api.deps import get_current_user
+from meshbay_hub.api.push import push_notification
from meshbay_hub.db.engine import get_db
-from meshbay_hub.db.models import GroupMember, Notification, User
+from meshbay_hub.db.models import GroupMember, Notification, User, UserPreference
router = APIRouter(prefix="/v1/notifications", tags=["notifications"])
@@ -157,9 +158,23 @@ async def create_notification(
conversation is a single line saying when it last spoke rather than forty
saying that it spoke.
- Returns None when the person muted this group: the point of muting is that
- nothing is created, not that something is created and hidden.
+ Returns None when the person muted this group, or turned every notification
+ off: the point of muting is that nothing is created, not that something is
+ created and hidden — and nothing created is nothing pushed to a phone.
+
+ The account-wide switch used to be read by the interface alone, which hid
+ the list while rows went on accumulating; with a phone that is told about
+ each row, a switch only the interface honours is a switch that does nothing.
"""
+ disabled = await db.execute(
+ select(UserPreference.value).where(
+ UserPreference.user_id == user_id,
+ UserPreference.key == "notifications_disabled",
+ )
+ )
+ if disabled.scalar() == "true":
+ return None
+
if group_id is not None:
muted = await db.execute(
select(GroupMember.muted).where(
@@ -185,6 +200,7 @@ async def create_notification(
existing.read = False
existing.created_at = datetime.now(UTC)
await db.flush()
+ await push_notification(db, existing)
return existing
notif = Notification(
@@ -193,4 +209,5 @@ async def create_notification(
)
db.add(notif)
await db.flush()
+ await push_notification(db, notif)
return notif