44 lines
1.7 KiB
Python
44 lines
1.7 KiB
Python
|
|
"""HTTP views for the voice app.
|
||
|
|
|
||
|
|
The WebRTC mesh itself is signalled over WebSocket (see consumers.py) + holds
|
||
|
|
no server state. The only HTTP surface is mute-state persistence: a my-sea voice
|
||
|
|
mute must survive in-sea navigation/refresh (which tear down + re-open the mesh),
|
||
|
|
so the mute is stamped on `User.voice_muted_at` and the client re-applies it on
|
||
|
|
the voice auto-rejoin. The timestamp also anchors the 3-min "muted too long →
|
||
|
|
auto-disconnect" window the client enforces.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import json
|
||
|
|
|
||
|
|
from django.contrib.auth.decorators import login_required
|
||
|
|
from django.http import JsonResponse
|
||
|
|
from django.utils import timezone
|
||
|
|
from django.views.decorators.http import require_POST
|
||
|
|
|
||
|
|
|
||
|
|
@login_required(login_url="/")
|
||
|
|
@require_POST
|
||
|
|
def voice_mute(request):
|
||
|
|
"""Persist the caller's voice mute state on `User.voice_muted_at`.
|
||
|
|
|
||
|
|
Body: JSON `{"muted": <bool>}`.
|
||
|
|
- muted=True → stamp `now` (anchors the 3-min auto-disconnect window).
|
||
|
|
- muted=False → clear (on unmute, a fresh manual join, or the 3-min
|
||
|
|
timeout's leave).
|
||
|
|
|
||
|
|
Idempotent — re-muting just re-stamps `now` (restarting the 3-min window),
|
||
|
|
which is the intended behaviour for an unmute→re-mute. Returns
|
||
|
|
`{ok, muted_at}` (ISO 8601 or null)."""
|
||
|
|
try:
|
||
|
|
payload = json.loads(request.body.decode("utf-8") or "{}")
|
||
|
|
except json.JSONDecodeError:
|
||
|
|
return JsonResponse({"error": "invalid_json"}, status=400)
|
||
|
|
muted = bool(payload.get("muted"))
|
||
|
|
request.user.voice_muted_at = timezone.now() if muted else None
|
||
|
|
request.user.save(update_fields=["voice_muted_at"])
|
||
|
|
return JsonResponse({
|
||
|
|
"ok": True,
|
||
|
|
"muted_at": (request.user.voice_muted_at.isoformat()
|
||
|
|
if request.user.voice_muted_at else None),
|
||
|
|
})
|