"""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": }`. - 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), })