def send_messages(sender, instance, created, **kwargs): """When there is a message in a conversation we need to send it to any subscribed participants.""" message = instance conversation = message.conversation topic = 'conversations:message' for subscription in ChannelSubscription.objects.recent().filter( user__in=conversation.participants.all()).distinct(): payload = ConversationMessageSerializer( message, context={ 'request': MockRequest(user=subscription.user) }).data send_in_channel(subscription.reply_channel, topic, payload) if created and message.is_thread_reply( ) and subscription.user != message.author: payload = ConversationMessageSerializer( message.thread, context={ 'request': MockRequest(user=subscription.user) }).data send_in_channel(subscription.reply_channel, topic, payload) # Send push notification and conversation updates when a message is created, but not when it is modified if not created: return tasks.notify_message_push_subscribers(message) # Send conversations object to participants after sending a message # (important for unread_message_count) # Exclude the author because their seen_up_to status gets updated, # so they will receive the `send_conversation_update` message topic = 'conversations:conversation' # Can be skipped for thread replies, as they don't alter the conversations object if message.is_thread_reply(): return for subscription in ChannelSubscription.objects.recent()\ .filter(user__in=conversation.participants.all())\ .exclude(user=message.author).distinct(): participant = conversation.conversationparticipant_set.get( user=subscription.user) payload = ConversationSerializer( participant, context={ 'request': MockRequest(user=subscription.user) }).data send_in_channel(subscription.reply_channel, topic, payload)
def send_reaction_update(sender, instance, **kwargs): reaction = instance message = reaction.message conversation = message.conversation topic = 'conversations:message' for subscription in ChannelSubscription.objects.recent() \ .filter(user__in=conversation.participants.all()).distinct(): payload = ConversationMessageSerializer(message, context={'request': MockRequest(user=subscription.user)}).data send_in_channel(subscription.reply_channel, topic, payload)
def send_thread_update(sender, instance, created, **kwargs): # Update thread object for user after updating their participation # (important for seen_up_to and unread_message_count) # Saves a few unnecessary messages if we only send on modify if created: return thread = instance.thread topic = 'conversations:message' payload = ConversationMessageSerializer(thread, context={'request': MockRequest(user=instance.user)}).data for subscription in ChannelSubscription.objects.recent().filter(user=instance.user): send_in_channel(subscription.reply_channel, topic, payload)
def list(self, request, *args, **kwargs): queryset = self.get_queryset() \ .exclude(conversation__latest_message_id=None) \ .annotate_unread_message_count() \ .annotate(conversation_latest_message_id=F('conversation__latest_message_id')) \ .select_related( 'conversation', 'conversation__latest_message', 'conversation__target_type', ) \ .prefetch_related( 'conversation__latest_message__reactions', 'conversation__participants', ) \ .order_by('-conversation__latest_message_id') queryset = self.filter_queryset(queryset) participations = self.paginate_queryset(queryset) conversations = [p.conversation for p in participations] messages = [ c.latest_message for c in conversations if c.latest_message is not None ] # Prefetch related objects per target type pickup_ct = ContentType.objects.get_for_model(PickupDate) pickup_conversations = [ item for item in conversations if item.target_type == pickup_ct ] pickups = PickupDate.objects. \ filter(id__in=[c.target_id for c in pickup_conversations]). \ prefetch_related('pickupdatecollector_set', 'feedback_given_by') applications_ct = ContentType.objects.get_for_model(Application) application_conversations = [ item for item in conversations if item.target_type == applications_ct ] applications = Application.objects. \ filter(id__in=[c.target_id for c in application_conversations]). \ select_related('user') issues_ct = ContentType.objects.get_for_model(Issue) issue_conversations = [ item for item in conversations if item.target_type == issues_ct ] issues = Issue.objects. \ filter(id__in=[c.target_id for c in issue_conversations]). \ prefetch_for_serializer(user=request.user) # Applicant does not have access to group member profiles, so we attach reduced user profiles my_applications = [a for a in applications if a.user == request.user] def get_conversation(application): return next(c for c in application_conversations if c.target_id == application.id) users = get_user_model().objects. \ filter(conversationparticipant__conversation__in=[get_conversation(a) for a in my_applications]). \ exclude(id=request.user.id) context = self.get_serializer_context() serializer = self.get_serializer(participations, many=True) message_serializer = ConversationMessageSerializer(messages, many=True, context=context) pickups_serializer = PickupDateSerializer(pickups, many=True, context=context) application_serializer = ApplicationSerializer(applications, many=True, context=context) issue_serializer = IssueSerializer(issues, many=True, context=context) user_serializer = UserInfoSerializer(users, many=True, context=context) meta, _ = ConversationMeta.objects.get_or_create(user=request.user) meta_serializer = ConversationMetaSerializer( meta, context=self.get_serializer_context()) return self.get_paginated_response({ 'conversations': serializer.data, 'messages': message_serializer.data, 'pickups': pickups_serializer.data, 'applications': application_serializer.data, 'issues': issue_serializer.data, 'users_info': user_serializer.data, 'meta': meta_serializer.data, })