from typing import List, Optional, Iterable import strawberry import strawberry_django as sd from strawberry import ID from strawberry_django.relay import DjangoCursorConnection from django.contrib.contenttypes.models import ContentType from core.graphql.filters.messaging import ConversationFilter, MessageFilter from core.graphql.types.messaging import ConversationType, MessageType from core.models.messaging import Conversation, Message from core.models.profile import TeamProfile, CustomerProfile @strawberry.type class Query: """Messaging queries""" conversation: Optional[ConversationType] = sd.node() conversations: List[ConversationType] = sd.field(filters=ConversationFilter) message: Optional[MessageType] = sd.node() messages: List[MessageType] = sd.field(filters=MessageFilter) @sd.connection( DjangoCursorConnection["ConversationType"], name="getMyConversations", description="Return conversations for the authenticated user (inbox)", filters=ConversationFilter, ) def get_my_conversations( self, info, include_archived: bool = False, ) -> Iterable["Conversation"]: """ Get all conversations for the current authenticated user. Returns conversations ordered by last message timestamp. """ # Get profile directly from context (not Django User model) profile = getattr(info.context.request, 'profile', None) if not profile: return Conversation.objects.none() # Determine the profile's content type content_type = ContentType.objects.get_for_model(type(profile)) # Build query queryset = Conversation.objects.filter( participants__participant_content_type=content_type, participants__participant_object_id=profile.id, ) # Filter archived conversations unless explicitly requested if not include_archived: queryset = queryset.filter(participants__is_archived=False) return queryset.prefetch_related( 'participants', 'participants__participant_content_type', ).distinct().order_by('-last_message_at', '-created_at') @sd.connection( DjangoCursorConnection["ConversationType"], name="getConversationsByEntity", description="Return conversations linked to a specific entity (Project, Service, Account, etc.)", filters=ConversationFilter, ) def get_conversations_by_entity( self, entity_type: str, entity_id: ID, ) -> Iterable["Conversation"]: """ Get all conversations linked to a specific entity. entity_type: Model name (e.g., 'Project', 'Service', 'Account') entity_id: UUID of the entity """ from django.apps import apps try: # Get the content type for the entity model = apps.get_model('core', entity_type) content_type = ContentType.objects.get_for_model(model) return Conversation.objects.filter( entity_content_type=content_type, entity_object_id=entity_id ).prefetch_related( 'participants', 'participants__participant_content_type', ).order_by('-last_message_at') except Exception: return Conversation.objects.none() @strawberry.field(description="Get unread message count for the authenticated user") async def unread_message_count(self, info) -> int: """ Get total unread message count across all conversations for the current user. """ from channels.db import database_sync_to_async # Get profile directly from context (not Django User model) profile = getattr(info.context.request, 'profile', None) if not profile: return 0 @database_sync_to_async def get_count(): # Determine the profile's content type content_type = ContentType.objects.get_for_model(type(profile)) # Sum unread counts from all participant records from core.models.messaging import ConversationParticipant from django.db.models import Sum total = ConversationParticipant.objects.filter( participant_content_type=content_type, participant_object_id=profile.id, is_archived=False ).aggregate(total=Sum('unread_count'))['total'] return total if total else 0 return await get_count() @sd.connection( DjangoCursorConnection["MessageType"], name="getMessagesByConversation", description="Return messages for a specific conversation", filters=MessageFilter, ) def get_messages_by_conversation( self, conversation_id: ID, include_system: bool = True, ) -> Iterable["Message"]: """ Get all messages for a specific conversation. """ queryset = Message.objects.filter(conversation_id=conversation_id) if not include_system: queryset = queryset.filter(is_system_message=False) return queryset.prefetch_related( 'read_receipts', 'sender_content_type', ).order_by('created_at')