mirror of
https://github.com/zulip/zulip.git
synced 2025-11-03 05:23:35 +00:00
We use the same strategy Zulip already uses for starred messages, namely, creating a new UserMessage row with the "historical" flag set (which basically means Zulip can ignore this row for most purposes that use UserMessage rows). The historical flag is ignored, however, in determining which users' browsers to notify about new reactions, and thus the user will get to see the reaction appear when they click a message (and any reactions other users later add, as well!). There's still something of a race here, in that if some users react to a message while the user is looking at the unsubscribed stream but before the user reacts to that message, those reactions will not be displayed to that user (so counts will be a bit lower, or something). This race feels small enough to ignore for now. Fixes #3345.
67 lines
2.8 KiB
Python
67 lines
2.8 KiB
Python
from __future__ import absolute_import
|
|
|
|
from django.http import HttpRequest, HttpResponse
|
|
from django.utils.translation import ugettext as _
|
|
from typing import Text
|
|
|
|
from zerver.decorator import authenticated_json_post_view,\
|
|
has_request_variables, REQ, to_non_negative_int
|
|
from zerver.lib.actions import do_add_reaction, do_remove_reaction
|
|
from zerver.lib.emoji import check_valid_emoji
|
|
from zerver.lib.message import access_message
|
|
from zerver.lib.request import JsonableError
|
|
from zerver.lib.response import json_success
|
|
from zerver.models import Reaction, UserMessage, UserProfile
|
|
|
|
@has_request_variables
|
|
def add_reaction_backend(request, user_profile, message_id, emoji_name):
|
|
# type: (HttpRequest, UserProfile, int, Text) -> HttpResponse
|
|
|
|
# access_message will throw a JsonableError exception if the user
|
|
# cannot see the message (e.g. for messages to private streams).
|
|
message, user_message = access_message(user_profile, message_id)
|
|
|
|
check_valid_emoji(message.sender.realm, emoji_name)
|
|
|
|
# We could probably just make this check be a try/except for the
|
|
# IntegrityError from it already existing, but this is a bit cleaner.
|
|
if Reaction.objects.filter(user_profile=user_profile,
|
|
message=message,
|
|
emoji_name=emoji_name).exists():
|
|
raise JsonableError(_("Reaction already exists"))
|
|
|
|
if user_message is None:
|
|
# Users can see and react to messages sent to streams they
|
|
# were not a subscriber to; in order to receive events for
|
|
# those, we give the user a `historical` UserMessage objects
|
|
# for the message. This is the same trick we use for starring
|
|
# messages.
|
|
UserMessage.objects.create(user_profile=user_profile,
|
|
message=message,
|
|
flags=UserMessage.flags.historical | UserMessage.flags.read)
|
|
|
|
do_add_reaction(user_profile, message, emoji_name)
|
|
|
|
return json_success()
|
|
|
|
@has_request_variables
|
|
def remove_reaction_backend(request, user_profile, message_id, emoji_name):
|
|
# type: (HttpRequest, UserProfile, int, Text) -> HttpResponse
|
|
|
|
# access_message will throw a JsonableError exception if the user
|
|
# cannot see the message (e.g. for messages to private streams).
|
|
message = access_message(user_profile, message_id)[0]
|
|
|
|
check_valid_emoji(message.sender.realm, emoji_name)
|
|
|
|
# We could probably just make this check be a try/except for the
|
|
# IntegrityError from it already existing, but this is a bit cleaner.
|
|
if not Reaction.objects.filter(user_profile=user_profile,
|
|
message=message,
|
|
emoji_name=emoji_name).exists():
|
|
raise JsonableError(_("Reaction does not exist"))
|
|
|
|
do_remove_reaction(user_profile, message, emoji_name)
|
|
|
|
return json_success()
|