Self-service avatar picker in Account
Users can now set their own profile picture instead of asking staff: POST /api/auth/avatar/ accepts a J-ID (or blank to clear) and reuses the same item resolution as the staff endpoint. The Account page gains a profile picture card with a searchable, paginated library grid — any item works (the thumbnail is used), the current avatar is marked, and the choice is confirmed before saving. Refreshing the signed-in user updates the shell avatar immediately. Verified against the dev server: set, clear, unknown J-ID -> 400, anonymous -> 401.
This commit is contained in:
@@ -98,6 +98,45 @@ class IsStaffUser(permissions.BasePermission):
|
||||
)
|
||||
|
||||
|
||||
def resolve_avatar_item(value):
|
||||
"""Turn a "J-42" / "42" string into a MediaItem.
|
||||
|
||||
Returns ``(item, error)``: an empty value clears the avatar (``None``,
|
||||
``None``), an unknown item returns an error message.
|
||||
"""
|
||||
value = str(value or "").strip()
|
||||
if not value:
|
||||
return None, None
|
||||
numeric = value[2:] if value.upper().startswith("J-") else value
|
||||
item = (
|
||||
MediaItem.objects.filter(pk=int(numeric)).first()
|
||||
if numeric.isdigit()
|
||||
else None
|
||||
)
|
||||
if item is None:
|
||||
return None, f"No library item {value}."
|
||||
return item, None
|
||||
|
||||
|
||||
class AvatarView(APIView):
|
||||
"""Self-service profile picture chosen from the library."""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
serializer = UserUpdateSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
item, error = resolve_avatar_item(
|
||||
serializer.validated_data.get("avatar_j_id")
|
||||
)
|
||||
if error:
|
||||
return Response({"detail": error}, status=status.HTTP_400_BAD_REQUEST)
|
||||
user = request.user
|
||||
user.avatar = item
|
||||
user.save(update_fields=["avatar"])
|
||||
return Response(UserSerializer(user, context={"request": request}).data)
|
||||
|
||||
|
||||
class UserViewSet(
|
||||
mixins.ListModelMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
@@ -133,22 +172,13 @@ class UserViewSet(
|
||||
user.role = data["role"]
|
||||
update_fields.append("role")
|
||||
if "avatar_j_id" in data:
|
||||
value = str(data.get("avatar_j_id") or "").strip()
|
||||
if not value:
|
||||
user.avatar = None
|
||||
else:
|
||||
numeric = value[2:] if value.upper().startswith("J-") else value
|
||||
item = (
|
||||
MediaItem.objects.filter(pk=int(numeric)).first()
|
||||
if numeric.isdigit()
|
||||
else None
|
||||
item, error = resolve_avatar_item(data.get("avatar_j_id"))
|
||||
if error:
|
||||
return Response(
|
||||
{"detail": error},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
if item is None:
|
||||
return Response(
|
||||
{"detail": f"No library item {value}."},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
user.avatar = item
|
||||
user.avatar = item
|
||||
update_fields.append("avatar")
|
||||
if update_fields:
|
||||
user.save(update_fields=update_fields)
|
||||
|
||||
Reference in New Issue
Block a user